BZOJ 3437:小P的牧场(DP+斜率优化)

                                                                     小P的牧场
【题目描述】
背景:小P 是个特么喜欢玩MC 的孩纸。。。
小P 在MC 里有n 个牧场,自西向东呈一字形排列(自西向东用1…n 编号),于是他就烦恼了:为了控制这n 个牧场,他需要在某些牧场上面建立控制站,每个牧场上只能建立一个控制站,每个控制站控制的牧场是它所在的牧场一直到它西边第一个控制站的所有牧场(它西边第一个控制站所在的牧场不被控制)(如果它西边不存在控制站,那么它控制西边所有的牧场),每个牧场被控制都需要一定的花费(毕竟在控制站到牧场间修建道路是需要资源的嘛~),而且该花费等于它到控制它的控制站之间的牧场数目(不包括自身,但包括控制站所在牧场)乘上该牧场的放养量,在第i 个牧场建立控制站的花费是ai,每个牧场i 的放养量是bi,理所当然,小P 需要总花费最小,但是小P的智商有点不够用了,所以这个最小总花费就由你来算出啦。
【输入格式】
第一行一个整数n 表示牧场数目
第二行包括n 个整数,第i 个整数表示ai
第三行包括n 个整数,第i 个整数表示bi
【输出格式】
只有一行,包括一个整数,表示最小花费
【样例输入】
4
2 4 2 4
3 1 4 2
【样例输出】
9
【样例解释】
选取牧场1,3,4 建立控制站,最小费用为2+( 2 + 1 * 1 ) + 4 = 9。
数据范围与约定
对于10%的数据,1 <= n <= 10
对于30%的数据,1 <= n <= 1000
对于100%的数据,1 <= n <= 1000000 , 0 < ai,bi <= 10000

分析:

经过思考可以得到这样一个式子

f[i,1]表示在第i个牧场建站且右边的牧场已全部控制的最小费用,f[i,0]表示第i个牧场未建站而从i到n的牧场都被控制的最小代价,倒着来进行处理。

f[i,1]:=min(f[i+1,1],f[i+1,0])+a[i];

f[i,0]:=min(f[j,1]+t); (i:=n..1,i<j<n)

其中t表示i..j-1的牧场被j控制的总代价,在动规循环同时求出,这样要用三层循环,显然这个效率并不好。

对DP进行改进,将牧场排列倒过来,同时改进f,f[i]表示在第i个牧场建站并之前的牧场都被控制的最小总代价,我们增加一个牧场n+1,令其建站和被控制代价为0。则有:

f[i]:=min(f[j]+t)+a[i];(i:=1..n+1,1<=j<i) 其中t表示j+1..i-1的牧场都被控制的总代价,求t可以采用一个很好地方法:

令s[i]表示1..i的a数组元素和,u[i]表示1..i的s数组元素和:

t=s[i-1]-s[j]+s[i-1]-s[j+1]+...+s[i-1]-s[i-2]

  =s[i-1]*(i-j-1)-(s[j]+s[j+1]+s[j+2]+..+s[i-2])

  =s[i-1]*(i-j-1)-(u[i-2]-u[j-1])

于是得到:

f[i]:=min(f[j]+s[i-1]*(i-j-1)-(u[i-2]-u[j-1]))+a[i];

时间效率为O(n^2),这样还不够,于是进行斜率优化,要注意向队列添加元素时队内最少元素数目不低于2个,我就在这里处理错了导致之前一直没AC。

代码:

program pasture;
var
  f:array[0..1000000]of int64;
  a,b,u,s,q:array[0..1000001]of int64;
  n,m,t,k,h,j:int64; i:longint;
function min(x,y:int64):int64;
begin
  if x<y then min:=x else min:=y;
end;
function cal1(x,y:longint):int64;
begin
  exit(f[x]-f[y]+u[x-1]-u[y-1]);
end;
function cal2(x,y:longint):int64;
begin
  exit(x-y);
end;
begin
  readln(n);
  for i:=n downto 1 do
   read(a[i]);
  readln;
  for i:=n downto 1 do
   read(b[i]);
  for i:=1 to n do
    s[i]:=s[i-1]+b[i];
  for i:=1 to n do
   u[i]:=u[i-1]+s[i];
  f[1]:=a[1]; q[1]:=1; h:=1; t:=1;
   for i:=2 to n+1 do
    begin
      while (h<t)and(cal1(q[h+1],q[h])<s[i-1]*cal2(q[h+1],q[h])) do inc(h);
      j:=q[h];
      f[i]:=f[j]+s[i-1]*(i-j-1)-(u[i-2]-u[j-1])+a[i];
      while (t-h>=1)and(cal1(i,q[t])*cal2(q[t],q[t-1])<=cal1(q[t],q[t-1])*cal2(i,q[t])) do dec(t);
      t:=t+1; q[t]:=i;
    end;
  writeln(f[n+1]);
end.
View Code

 

posted @ 2015-03-13 22:10  QTY_YTQ  阅读(197)  评论(0编辑  收藏  举报