Knapsack probleminin bir dəyişməsi: Java-da "Bölüşmə Bərabər Alt Sum Sum" problemini necə həll etmək olar

Yeniləmə: Dinamik proqramlaşdırma həllinin kosmik mürəkkəbliyini optimallaşdırmaq barədə məlumatı aşağıdakı məqaləmdə tapa bilərsiniz.

Bundan əvvəl Knapsack probleminin (KP) dinamik proqramlaşdırma ilə həll edilməsi haqqında yazmışdım. Bu barədə burada oxuya bilərsiniz.

Bu gün bir KP bir dəyişikliyini müzakirə etmək istərdim: bölmə problemi alt məbləğə bərabərdir. Bu problemi ilk dəfə Leetcode-da gördüm - KP ilə tanış olmaq və bu barədə yazmaq üçün səbəb oldu.

Bu problem (qismən nümunəsiz çoxalır):

Yalnız müsbət ədədləri ehtiva edən boş olmayan bir sıra üçün, elementlərin cəminin hər iki alt hissədə eyni olması üçün serialın iki alt hissəyə bölünməyini təyin etməlisiniz.

Məhdudiyyətlər və nümunələr ilə problemlərin tam təsvirini Leetcode problemində tapa bilərsiniz.

Dinamik proqramlaşdırma

KP-də olduğu kimi, bunu dinamik proqramlaşdırma ilə həll edirik. KP-nin bir dəyişməsi olduğundan, məntiq və metodologiya bir-birinə bənzəyir.

Həll yolu

Çözümümüzü Boolean dəyəri qaytaran bir metodla yerləşdirəcəyik - əgər serial bərabər alt hissələrə bölünə bilərsə, doğrudur, əksinə.

Addım 1: ümumi tənzimlənmədən qorunma

Ardıcıl olaraq, serialdakı bütün nömrələr tək bir cəmi artırsa, yalanı qaytara bilərik. Dizi bərabər bir cəm olduqda davam edirik.

Addım 2: masanın yaradılması

Sonra masanı yaradırıq.

Cədvəl sətirləri nəzərə alınacaq sıra elementlərinin miqdarını, cədvəl sütunları isə əldə etmək istədiyimiz məbləği göstərir. Cədvəl dəyərləri sadəcə bir sıra sıra elementlərindən (sıra) istifadə edərək cəmin (sütunun) təyin oluna biləcəyini göstərən boolean dəyərlərdir.

Xüsusilə, i sıra 0-dan (i-1) qədər olan sıra elementlərinin bir dəstini təmsil edir. 1-dən bu əvəzlənmənin səbəbi 0 satırının boş elementlər toplusunu təmsil etməsidir. Buna görə, 1-ci sıra ilk sıra elementi (indeks 0), ilk iki sıra elementləri üçün 2-ci sıra (indekslər 0-1) və s. Ümumilikdə 0 daxil olmaqla n + 1 sıra yaradırıq.

Yalnız serialın tam yarısını ümumiləşdirə biləcəyimizi bilmək istəyirik. Buna görə yalnız 0 daxil olmaqla totalSum / 2 + 1 sütunu yaratmalıyıq.

Addım 3: masanı əvvəlcədən doldurun

Cədvəlimizdəki baza halları üçün girişləri doldurmaqla dərhal başlaya bilərik - satır 0 və sütun 0.

Birinci sətirdə hər giriş - birincisi istisna olmaqla - səhv olmalıdır. Unutmayın ki, birinci sətir boş bir dəsti təmsil edir. Əlbəttə ki, hədəf məbləği - sütun nömrəsini - 0-dan başqa bir şey təyin edə bilmərik.

Birinci sütunda hər giriş doğru olmalıdır. İşləməyimiz lazım olan elementlərin miqdarından asılı olmayaraq cəmi 0-a çatmağı bacarırıq.

Addım 4: masanın qurulması (problemin özəyi)

Aşağıdakı üç şərtdən biri yerinə yetirildiyi təqdirdə i və s sətrindəki cədvəldəki giriş doğrudur (mümkündür):

  1. I-1 və j sütundakı giriş doğrudur. Sətir nömrəsinin nəyi təmsil etdiyini xatırlayın. Beləliklə, hazırda mövcud olduğumuz elementlərin alt dəsti ilə müəyyən bir məbləği əldə edə bilsək, cari elementlər dəsti ilə də əldə edə bilərik - sadəcə əlavə elementlərdən istifadə etmədən. Mənasızdır. Bu prevRowIsTrue adlandıraq.
  2. Mövcud element tam əldə etmək istədiyimiz məbləğdir. Bu da mənasız həqiqətdir. Buna "isExactMatch" deyək.
  3. Yuxarıdakı iki şərt yerinə yetirilmədiyi təqdirdə hədəfimizə çatmaq üçün bir yolumuz var. Burada cari elementi - əvvəlki cərgədəki elementlər dəsti ilə müqayisədə mövcud cərgədəki elementlər dəstindəki əlavə elementi istifadə edirik və hədəf məbləğin qalan hissəsinə çata biləcəyimizi yoxlayırıq. Buna canArriveAtSum deyək.

3 şərti açaq. Mövcud cəmi yalnız hədəf cəmimizdən aşağı olduqda istifadə edə bilərik. Bunlar eynidirsə, 2 şərt yerinə yetiriləcəkdir. Daha böyükdürsə, istifadə edə bilmərik. Buna görə ilk addım, mövcud maddənin olduğundan əmin olmaqdır

Mövcud elementimizi istifadə etdikdən sonra hədəf məbləğimizin qalan hissəsi qalır. Daha sonra yuxarıdakı sətirdə müvafiq yazını yoxlamaqla bunun əldə edilə biləcəyini yoxlayırıq.

Normal KP kimi, masamızı addım-addım aşağıdan yuxarıya doğru qurmaq istəyirik. Əsas işlərdən son həllimizə gələnə qədər başlayırıq.

Addım 5: cavabı qaytarın

Biz yalnız geri mat [nums.length] [totalSum / 2] qayıdırıq.

İş kodu

Oxuduğunuz üçün təşəkkür edirik!