bzoj 1597 [Usaco2008 Mar]土地购…

来源:互联网 发布:移动进销存软件 编辑:程序博客网 时间:2024/05/20 22:01

Description

农夫John准备扩大他的农场,他正在考虑N (1 <= N <=50,000) 块长方形的土地. 每块土地的长宽满足(1 <= 宽 <=1,000,000; 1 <= 长 <= 1,000,000).每块土地的价格是它的面积,但FJ可以同时购买多快土地. 这些土地的价格是它们最大的长乘以它们最大的宽, 但是土地的长宽不能交换.如果FJ买一块3x5的地和一块5x3的地,则他需要付5x5=25. FJ希望买下所有的土地,但是他发现分组来买这些土地可以节省经费.他需要你帮助他找到最小的经费.

Input

* 第1行: 一个数: N * 第2..N+1行: 第i+1行包含两个数,分别为第i块土地的长和宽

Output

* 第一行: 最小的可行费用.

Sample Input

4
100 1
15 15
20 5
1 100

输入解释:

共有4块土地.

Sample Output


500

HINT


FJ分3组买这些土地: 第一组:100x1, 第二组1x100, 第三组20x5 和 15x15 plot. 每组
的价格分别为100,100,300, 总共500.

 

这题很早做的,想再推下斜率优化的题。结果果然爆掉了。。。

以前一开始连DP的思路都没有。。更别提优化了。。

//================================================================================================

对于读入的随机数据想看出DP的方法是很难的。但是考虑要用到的是某些块里面最大的和最小的值,考虑排序。

先用一个关键字排序。长和宽没有本质区别。

可以发现,对于两个矩形,如果第一个矩形的长和宽都不大于第二个,那么买第二个的时候就可以顺带买掉第一块,因为这个时候第一块是没有任何代价的(我这里称之为第一块被第二块包含)。

那么去掉了所有“被包含”的矩形后,经过排序的长宽序列会有个很有用的性质:长度序列不降的同时宽度序列不增(反之亦同)。

那么动归的阶段性就很明显了:购买前i块的消费。

转移方程为:f[i]=min(f[j]+a[j+1]*b[i])  (0<=j<i)  //其中a[i]表示第i块的宽,b为长。

这里定义a数组经过排序后是单调不升的,b数组相反。这个和下面程序的表达不同。。。注意注意!!

为了操作方便,可以在事先把a数组整体前移一位。则方程可以表示为:

f[i]=min(f[j]+a[j]*b[i])

 

列出了转移方程是不是就解决了?如果每次遍历0~i-1必然超时、、

考虑两个决策f[j],f[k]并假设j<k。

如果对于f[i],从f[j]转移来比从f[k]转移来更优,那么有:

f[j]+a[j]*b[i]<f[k]+a[k]*b[i]

移项得:

b[i]<(f[k]-f[j])/(a[j]-a[k])

为方便表示,定义d(j,k)=(f[k]-f[j])/(a[k]-a[j])

这个式子有什么用呢?

考虑:b[i]为不降的序列。所以当存在i,j,k三个决策时,并且满足i<j<k。如果d(i,k)>d(j,k)那么j决策就是无用的决策。因为如果i决策都不如k优了,那么就是说d(i,k)<b[now],那么d(j,k)肯定也小于b[now]。也就是说决策i比决策j的生命力更强、

那么,就可以开一个单调队列,存可能转移的决策,并使得d值递增。

额。。。就讲这么多了。。。剩下的自己YY吧。。

程序里面维护队列就两句话,而且为了省变量,判断句又臭又长。。。

 

注意:

1.在更新当前决策的时候,往后推队列的头指针,使得队列头两个的d值满足b[i]的约束。

2.在更新完当前决策后都要入队,入队的时候为了保持d值的单调性要往前推尾指针。

3.推尾指针的时候队尾两个分别与当前决策点求出d值,比较大小(一开始变成求d(尾两个)和d(尾,当前)了。。于是就WA掉了)。

4.搞清楚大小关系!!!!!

 

 

ACCODE

 

program bzoj_1597;
var a,b,f:array[0..50000] of int64;
   q:array[1..50000] of longint;
   i,n,tail,l,r:longint;
//============================================================================
procedure qsort(l,r:longint);
var i,j:longint;
   k1,k2,tt:int64;
begin
  k1:=a[(l+r) shr 1]; k2:=b[(l+r) shr 1]; i:=l;j:=r;
  repeat
    while(a[i]<k1) or ((a[i]=k1) and(b[i]>k2)) do inc(i);
    while(a[j]>k1) or ((a[j]=k1) and(b[j]<k2)) do dec(j);
    ifi<=j then
    begin
     tt:=a[i]; a[i]:=a[j]; a[j]:=tt;
     tt:=b[i]; b[i]:=b[j]; b[j]:=tt;
     inc(i); dec(j);
    end;
  until i>j;
  if j>l then qsort(l,j);
  if i<r then qsort(i,r);
end;
//============================================================================
begin
  readln(n);
  for i:=1 to n do readln(a[i],b[i]);
  qsort(1,n); tail:=1;
  for i:=2 to ndo   //去掉被包含的矩形。
  begin
    while(tail>0) and (b[i]>=b[tail]) dodec(tail);
   inc(tail);
   a[tail]:=a[i]; b[tail]:=b[i];
  end; n:=tail;
  for i:=0 to n-1 do b[i]:=b[i+1];
  l:=1; r:=1; q[1]:=0; f[0]:=0;
  for i:=1 to n do
  begin
    while(l<r) and(a[i]*(b[q[l]]-b[q[l+1]])>f[q[l+1]]-f[q[l]]) doinc(l);  //推头指针。
   f[i]:=f[q[l]]+b[q[l]]*a[i];
    while(l<r) and
     ((f[i]-f[q[r-1]])*(b[q[r]]-b[i])>=(f[i]-f[q[r]])*(b[q[r-1]]-b[i]))do  //推尾指针。
       dec(r);
    inc(r);q[r]:=i;
  end; writeln(f[n]);
end.

0 0
原创粉丝点击