目录见右上
站内文章欢迎各位转载,但是请注明出处(毕竟码字很辛苦哈)。

主题

Pb的生日蛋糕

原题:(权限限制没有超链接)

题目描述Description

Pb的好朋友的生日就要到了,Pb决定给他送一个自己做的生日蛋糕。

现在Pb手里拥有一些圆柱形蛋糕,他想将他们组合起来做成一个大的生日蛋糕,也就是将一些圆柱形蛋糕堆放在另一些上面。每个蛋糕现在都有一个底面半径,高和美味度。Pb规定堆放在上层的蛋糕的美味度和体积都必须严格大于下层的蛋糕。并且我们约定没有任何两个蛋糕的美味度相同。

Pb现在会按美味度由小到大的顺序给你每个蛋糕的半径和高,他想知道他所能堆出的生日蛋糕体积最大为多少?注意到蛋糕的美味度具体是多少对我们没有任何影响。

输入描述Input Description

第一行一个整数N,表示Pb有多少个蛋糕

接下来N行,每行两个整数,表示每个蛋糕的半径和高。

输入顺序即为蛋糕美味度由小到大的顺序。

输出描述Output Description

输出一个浮点数,表示最大体积。如果你的答案和标准答案的误差不超过10^−3则认为你的答案是正确的。保证标准答案输出9位小数。

样例输入Sample Input

样例输入1:

2

100 30

40 10

样例输入2:

1 1

9 7

1 4

10 7

样例输出Sample Output

样例输出1:

942477.796077000

样例输出2:

3983.539484752 

数据范围及提示Data Size & Hint

此题共4个子任务,你只有通过一个子任务中的所有测试点,才能得到该子任务的分值。

子任务1:9分,保证N<=10。

子任务2:19分,保证N<=1000。

子任务3:22分,保证r,h<=100。

子任务4:50分,无特殊约定。

对于所有测试数据,保证N<=100000, r,h<=10000。


个人解法:

首先,我们可以很容易想到朴素的算法——DP。用f[i]表示当前以i为顶端的最大体积,那么我们很容易得出f[i]=max(f[j])+v[i],其中1<=j<=i-1,v[j]<v[i]。

可是i与j的规模都达到了100000,O(n^2)的算法显然超时。

有趣的是,子任务3给了我们一点提示。

我们发现,每一次DP我们都是找到在当前i之前的最大的f[j],有没有什么可以优化一下这一循环呢?答案是肯定的。

我们可以以v[i]作为下标构造一个数组a。在每一次DP完之后就把f[i]扔到这个以v[i]为下标的数组里。那么我们得到的这一个数组中,就是i之前的所有f[i]的值。接下来我们找最大值就方便多了。我们知道当前的v[i],所以我们只需要在a[1]-a[v[i]-1]的所有f[i]中找到最大的一个f[i],这用树状数组、线段树等诸如此类者都可以。

最后的答案就是a[1]-a[max(v[i])]的最大f[i]。

当然,由于r,h的规模达到了10000,也就意味着v[i]规模达到了10^12。又由于我们只需要体积的大小顺序就可,因此用离散化把v缩小到n的规模。


代码如下:

type

arr110000=array[0..110000] of int64;


var

f,v,p,b,c:arr110000;//f在之前是离散数组,之后是DP数组。v是体积,p、b是离散数组,c是树状数组

r,h,j:int64;

n,i:longint;

s:double;


function lowbit(x:longint):longint;//树状数组函数

begin

  exit(x and (-x));

end;


function max(a,b:int64):int64;//较大值

begin

  if a>b then exit(a);

  exit(b);

end;


procedure change(i:longint;data:int64);//修改

begin

  while i<=n do

  begin

   c[i]:=max(c[i],data);

   inc(i,lowbit(i));

  end;

end;


function getsum(i:longint):int64;//统计1到i的最大值

begin

  getsum:=0;

  while i>0 do

  begin

   getsum:=max(getsum,c[i]);

   dec(i,lowbit(i));

  end;

end;


procedure swap(var a,b:int64);//交换+排序

var

  c:int64;

begin

  c:=a;

  a:=b;

  b:=c;

end;

procedure sort(l,r:longint);

var

  tl,tr:longint;

  m:int64;

begin

  tl:=l;

  tr:=r;

  m:=f[(tl+tr) shr 1];

  repeat

   while f[tl]<m do inc(tl);

   while f[tr]>m do dec(tr);

   if tl<=tr then

   begin

    swap(f[tl],f[tr]);

    swap(b[tl],b[tr]);

    inc(tl);

    dec(tr);

   end;

  until tl>tr;

  if tl<r then sort(tl,r);

  if l<tr then sort(l,tr);

end;


begin

read(n);

for i:=1 to n do

begin

  read(r,h);

  v[i]:=r*r*h;//数据处理

  f[i]:=v[i];

end;

for i:=1 to n do

  b[i]:=i;


sort(1,n);//排序离散


p[b[1]]:=1;

for i:=2 to n do

  if v[b[i]]>v[b[i-1]] then p[b[i]]:=p[b[i-1]]+1//下标离散

   else p[b[i]]:=p[b[i-1]];


fillchar(f,sizeof(f),0);

for i:=1 to n do

begin

  j:=getsum(p[i]-1);//取出1-p[i]-1中的最大的f[j]

  f[i]:=j+v[i];//计算当前f[i]

  change(p[i],f[i]);//把f[i]放入树状数组

end;

j:=getsum(n);//统计答案

s:=j*pi;

writeln(s:0:9);

end.


受益匪浅。第一次接触到了除了四边形不等式以外的DP加速方法。这么说来还有许多强有力的优化呢。

如果有什么优化,希望各位大神提出。

DP路上,希望与你们共同进步。

评论
©主题 —— Powered by LOFTER