k altmassivə bölünmə#9020

k altmassivə bölünmə (Həll təhlili)

Optimal qiyməti ikili axtarış vasitəsilə tapacağıq. Aydındır ki, o, -dən kiçik ola bilməz (çünki massivdəki rəqəmlər təbii ədədlərdir) və bütün elementlərin cəmini keçə bilməz. Sərhədləri quraq: sol = 1, sağ = bütün massiv ədədlərinin cəmi. Onda cavab mütləq [sol; sağ] aralığında yerləşir.

Yardımçı alt məsələyə baxaq: massivə elə altmassivə bölmək olarmı ki, bütün altmassivin içindəki maksimal cəm verilmiş s_0 dəyərindən artıq olmasın? Əgər massivdə s_0-dan böyük ən azı bir element varsa, belə bölmə mümkün deyil. Digər hallarda, element cəmi s_0-dan artıq olmayan altmassivləri ardıcıl formalaşdıracağıq. Nəticədə -dan çox olmaması şərtilə altmassiv alınarsa, belə bölmə mümkündür.

Misal

İkinci misalda, müxtəlif s_0 dəyərlərində massivə altmassivlərə bölməyi nəzərdən keçirək:

Alqoritmin reallaşdırılması

MAX = 10^6 sabitinin və işçi massiv -in elan edilməsi.

#define MAX 1000000
int m[MAX];

f funksiyası elə qaytarır ki, massivə elə altmassivə bölmək olar ki, bütün altmassivlərin içində maksimal cəm verilmiş s_0 dəyərindən artıq olmasın.

int f(long long s0)
{
  long long sum = 0;
  int cnt = 1;
  for (int i = 0; i < n; i++)
  {

Əgər massivdə hansısa element s_0-dan böyükdürsə, belə bölmə mümkün deyil.

    if (m[i] > s0) return 0;

Cari altmassivin cəmini hesablamaq.

    sum += m[i];

Cari cəm s_0-ı üstələdikdə, altmassivlərin sayğacını artırırıq və növbəti altmassivin cəmini toplamağa başlayırıq.

    if (sum > s0)
    {
      sum = m[i];
      cnt++;
    }
  }

Əgər massiv -dan çox olmayan altmassivə bölünə bilərsə, deməli, onu tam k~(k \le n) altmassivə bölmək olar.

  if (cnt <= k) return 1;
  return 0;
}

Proqramın əsas hissəsi. Giriş verilənlərini oxuyuruq. sağ dəyişənində bütün massiv elementlərinin cəmini hesablayırıq.

scanf("%d %d", &n, &k);
left = 1; right = 0;
for (i = 0; i < n; i++)
{
  scanf("%d", &m[i]);
  right += m[i];
}

Axtarılan minimal maksimum altmassiv cəm [sol; sağ] aralığında yerləşir. İkili axtarışı bu intervalda başlatırıq.

while (left < right)
{
  mid = (left + right) / 2;
  if (f(mid) == 1)
    right = mid;
  else
    left = mid + 1;
}

Cavabı çap edirik.

printf("%lld\n", left);