Tikinti konstruktoru#10869

Tikinti konstruktoru (Həll təhlili)

Binaları onların hündürlüyünə görə çeşidləyək, belə ki h_1 \le h_2 \le ... \le h_n bərabərsizliyi ödənilsin. Əgər bir neçə bina eyni hündürlüyə malikdirsə, onları əlavə və ya çıxarmaq qiymətinə görə azalan şəkildə də sıralayırıq.

Bütün binaları h_x~(1 \le x \le n) hündürlüyə gətirmək üçün lazım olan qiyməti nəzərdən keçirək. O, bərabərdir

f(x) = \sum_{i=1}^n |h_i - h_x| \cdot c_i

Bu qiyməti təsvir edən funksiyası qapalı və qlobal minimuma malikdir ki, bu da üçlük axtarışı ilə tapıla bilər.

Nümunə

İlk nümunəni nəzərdən keçirək. Binaları onların hündürlüyünə görə çeşidləyək:

Binaların mümkün bütün hündürlükləri üçün funksiyasının qiymətlərini tapaq:

funksiyasının minimum qiyməti x = 3 olduğu halda əldə edilir; bu halda f(3) = 110.

Alqoritmin tətbiqi

Hər bir bina hündürlüyünə və mərtəbəni əlavə və ya çıxarmaq üçün xərci var. Bina məlumatları massivində saxlanılır.

#define MAX 100001
struct Building
{
  int h, cost;
};
Building b[MAX];

cmp funksiyası binaların çeşidlənməsi üçün komparator rolunu oynayır. İlk növbədə binalar hündürlüklərə görə artan şəkildə, eyni hündürlükdə olan binalar isə xərclərinə görə çeşidlənir.

int cmp(Building a, Building b)
{
  if (a.h == b.h)
    return a.cost < b.cost;
  return a.h < b.h;
}

f funksiyası bütün binaları bina ilə eyni hündürlüyə gətirmək üçün lazım olan qiymətləri hesablayır.

long long f(int x)
{
  long long ret = 0;
  for (int i = 1; i <= n; i++)
    ret += 1LL * abs(b[x].h - b[i].h) * b[i].cost;
  return ret;
}

ternary_search funksiyası üçlük axtarışı vasitəsilə [left; right] intervalında -ın minimal olduğu qiymətini tapır. Başlanğıcda interval [left; right] = [1; n] olar.

long long ternarySearch(int left, int right)
{
  while (right - left >= 3)
  {
    int a = left + (right - left) / 3;
    int b = left + 2 * (right - left) / 3;
    if (f(a) > f(b))
      left = a;
    else
     right = b;
  }

və arasındakı fərq -dən böyük deyil. Bu halda [left; right] intervalında -ın minimal qiymətini tam iterasiya ilə tapırıq.

  long long res = f(left++);
  while (left <= right)
    res = min(res, f(left++));
  return res;
}

Proqramın əsas hissəsi. Giriş məlumatlarını oxuyuruq.

scanf("%d", &n);
for (i = 1; i <= n; i++)
  scanf("%d", &b[i].h);
for (i = 1; i <= n; i++)
  scanf("%d", &b[i].cost);

Binaları çeşidləyirik.

sort(b + 1, b + n + 1, cmp);

Cavabı hesablayırıq və çıxış edir. Binalar -dən -ə kimi nömrələnir.

printf("%lld\n", ternarySearch(1, n));