1

Тема: Динамика + Выпуклая Оболочка

Я встречал такое в разборах некоторых задач ( напр. USACO Mar08 "Land Acquisition"), там сказано что для  достижения асимптотики O(N) нужно использовать выпуклую оболочку, но как?
Из исходного кода я ничего не понял sad .
Можете немного объяснить?

2

Re: Динамика + Выпуклая Оболочка

А на саму задачу ссылку можно?

3

Re: Динамика + Выпуклая Оболочка

http://tjsct.wikidot.com/usaco-mar08-gold

4

Re: Динамика + Выпуклая Оболочка

Дело в том , что там выпуклая используется не поэтому !
Просто во время просчёта динамики нам нужно быстро находить оптимум.
Если представить точки динамики как функцию,то нужно найти наиболее выгодную точку,что бы получить наилучший ответ .
А это делается с помощью пару строк из алгоритма выпуклой оболочки,где мы сравниваем три подряд точки и выпихиваем среднюю если не выгодно ! Так как каждая точка будет использоваться ровно один раз,то и получаем асимптотику O(n).

5

Re: Динамика + Выпуклая Оболочка

Я алгоритм вроде понял cool, но непонятно было какое отношение имеет выпуклая оболочка .Теперь все ясно.
Спасибо big_smile

6

Re: Динамика + Выпуклая Оболочка

Я не понял псевдокода в разборе и вот мой вариант решения за квадрат:

Отсортируем все участки по возрастанию ширины. Идем слева направо. Пусть ans(i) это минимальная стоимость покупки всех участков с номерами от 1 до i(нумеруем с 1, а ans(0)=0).
Тогда получаем переход:

ans(i)=min(max(length[j],..,length[i])*width[i]+ans(j-1)) по всем 1<=j<=i

Как ускорить это решение до сложности O(NlogN) или даже O(N) после сортировки(как написано в разборе)?

7

Re: Динамика + Выпуклая Оболочка

Ну блин ,... у тебя полное динамика понятное дело .
Во первых ! Для начала ускорение,это избавиться от ненужного вначале ..
то есть если есть (X1,Y1),(X2,Y2) то нам не надо использовать (X1,Y1), если (X2>=X1 && Y2>=Y1) ===>
если есть (5,4) и (6,4) то (5,4) можно удалить спокойно так как на ответ это не влияет,(6,4) поглощает (5,4) big_smile

Ну а далее эта динамика только она не бегает по всем 1<=j<=i .. она бегает по заведомо выбранным оптимальным ans(j)...

#include<algorithm>
#include<iostream>
using namespace std;

#define forr(i,x,y) for(int i=(int)(x); i<(int)(y); i++)
#define mem(a,b) memset(a,b,sizeof(a))
#define x first
#define y second

typedef pair<long long,long long> vll;

pair<int,int> a[50010];
vll f[50010];
int N,n=0,l=0,r=1;
long long fc=0,w=0;

bool compare(pair<int,int> a,pair<int,int> b) {
 if(a.x<b.x) return true;
 if(a.x>b.x) return false;
 if(a.y<b.y) return true;
 return false;
}

bool lesss(vll a,vll b,vll c) {
 return ((a.y-b.y)*(b.x-c.x)<=(b.y-c.y)*(a.x-b.x));
}

int main() {
 freopen("acquire.in","r",stdin);
 freopen("acquire.out","w",stdout);
 scanf("%d",&N);
 mem(a,0); mem(f,0); 
 forr(i,0,N) scanf("%d %d",&a[i].x,&a[i].y); 
 sort(a,a+N,compare);
 forr(i,0,N) { n++;
  while(n && a[i].x>=a[n-1].x && a[i].y>=a[n-1].y) n--;
  a[n]=a[i];
 }
 f[(n++)-n]=make_pair(a[0].y,0);
 forr(i,0,n) { fc=-1;
  forr(j,l,r) {
   w=f[j].x*a[i].x+f[j].y;
   if(w<fc || fc<0) { fc=w; l=j; } else break;
  }
  if(i<n-1) {
   f[r++]=make_pair(a[i+1].y,fc);
   while(r-l>2 && lesss(f[r-3],f[r-2],f[r-1])) { f[r-2]=f[r-1]; r--; } 
  } 
 }
 cout<<fc; 
}

Вот мой CODE tongue

8

Re: Динамика + Выпуклая Оболочка

Спасибо за код. Избавление от ненужных это понятно. Я не могу понять как избавиться тут:

ans(i)=min(max(length[j],..,length[i])*width[i]+ans(j-1)) по всем 1<=j<=i

от

max(length[j],..,length[i])

9 Отредактировано brainail (2009-07-18 21:32:08)

Re: Динамика + Выпуклая Оболочка

то есть я так понял у тебя найти максимальное в промежутке ???? если да,то блина просто структуру заведи,что бы за О(1) искать минимальное на промежутке .. если не так понял то извини . smile

10

Re: Динамика + Выпуклая Оболочка

Нет, я имею ввиду не это. В разборе они это как-то переписывают и избавляются в формуле перехода от этого максимума. Ведь для того чтобы сделать решение за О(Н) после сортировки, нужно привести переход к виду ans(i)=k*x+l. И этому максимуму тут не место.

11

Re: Динамика + Выпуклая Оболочка

Нет,ну во-первых не обязан ты писать за О(Н),можешь и за О(Нлог(Н)),сильно не повлияет.
Во-вторых они это делают так,
пусть ANS(i) это ответы для первых I точек,X(i), Y(i) - размеры блоков.
Тогда что мы делаем на каждом шаге
представляем как бы точки вида - (Y(i),ANS(i))

f[(n++)-n]=make_pair(a[0].y,0);
......make_pair(a[i+1].y,fc)

Вначале это ANS = 0, Y(0)
Далее это будет Y(i), ANS(i)
Теперь на каждый момент I, мы будем иметь границу (L,R) .
На каждом шаге когда мы хотим посчитать ANS(i), мы бегаем только по J = L..R .
Так же мы на каждом шаге двигаем эти (L,R), что бы поддерживать оптимальность.
Их мы двигаем так.
L = максимальному оптимальному J на данный момент I.
R = двигаем вниз пока ответ хуже чем надо.
И того в итоге мы сделаем ~~O(N) шагов ...
Вроде это видно из CODE.

12

Re: Динамика + Выпуклая Оболочка

В этом куске кода:

forr(j,l,r) {
   w=f[j].x*a[i].x+f[j].y;
   if(w<fc || fc<0) { fc=w; l=j; } else break;
  }

Предполагается что если если A это номер участка f[j] в массиве a, то a(A).y=max(a(A).y,...,a(i).y), т.к. умножая на f[j].x мы умножаем на максимальную высоту участка от A до i. А почему тогда между A и i не может быть участка выше?

13

Re: Динамика + Выпуклая Оболочка

f[r++]=make_pair(a[i+1].y,fc);
   while(r-l>2 && lesss(f[r-3],f[r-2],f[r-1])) { f[r-2]=f[r-1]; r--; } 

Вот этими строчками мы добиваемся что бы L и R были оптимальны.
Здесь мы что делаем,сами точки (f(i).x,f(i).y) представляются как бы на плоскости,
и мы уменьшаем R пока заведомо известно что данная точка (f(R).x,f(R).y) не даст оптимума,то есть она будет лишняя,поэтому мы её просто убираем,
точно так же делается и при построении выпуклой оболочки,когда у нас идёт неверный поворот,то мы удаляем лишнюю точку как и здесь.
(

bool lesss(vll a,vll b,vll c) {
 return ((a.y-b.y)*(b.x-c.x)<=(b.y-c.y)*(a.x-b.x));
}

Эта процедура определяет лишняя она или нет.
)
Поэтому у нас будет сложность ~~О(Н) .....:/