小H的小屋
【问题描述】
小H发誓要做21世纪最伟大的数学家。他认为,做数学家与做歌星一样,第一步要作好包装,不然本事再大也推不出去。为此他决定先在自己的住所上下功夫,让人一看就知道里面住着一个“未来的大数学家”。
为了描述方便,我们以向东为x轴正方向,向北为y轴正方向,建立平面直角坐标系。小H的小屋东西长为100Hil(Hil是小H自己使用的长度单位,至于怎样折合成“m”,谁也不知道)。东墙和西墙均平行于y轴,北墙和南墙分别是斜率为k1和k2的直线,k1和k2为正实数。北墙和南墙的墙角处有很多块草坪,每块草坪都是一个矩形,矩形的每条边都平行于坐标轴。相邻两块草坪的接触点恰好在墙上,接触点的横坐标被称为它所在墙的“分点”,这些分点必须是1到99的整数。
小H认为,对称与不对称性的结合才能充分体现“数学美”。因此,在北墙角要有m块草坪,在南墙角要有n块草坪,并约定m≤n。如果记北墙和南墙的分点集合分别为X1,X2,则应满足X1X2,即北墙的任何一个分点一定是南墙的分点。
由于小H目前还没有丰厚的收入,他必须把草坪的造价降到最低,即草坪的占地总面积最小。你能编程帮他解决这个难题吗?
【输入文件】
仅一行,包含4个数k1,k2,m,n。k1和k2为正实数,分别表示北墙和南墙的斜率,精确到小数点后第一位。m和n为正整数,分别表示北墙角和南墙角的草坪的块数。
【输出文件】
一个实数,表示草坪的最小占地总面积。精确到小数点后第一位。
【约定】
Ø 2≤m≤n≤100
Ø 南北墙距离很远,不会出现南墙草坪和北墙草坪重叠的情况
【样例输入】
0.5 0.2 2 4
【样例输出】
3000.0
-------------------------------------------------------------------------------------------------------------------今天一晚上就做了这一题,不能说很难,朴素的DP很容易找到,关键在于优化,对于函数f(j,k),当i确定时,f-k是单峰函数,设峰值对应的k=k',会有性质:j越大,k'越大,根据这一性质,可以把时间复杂度从O(L^2MN^2)降低到O(L^2MN),这样就可以AC了。
-------------------------------------------------------------------------------------------------------------------
当然,这题还有更优的算法,如果贪心(利用题目中均摊的性质)的话,复杂度可以降到线性,但那个意义不大,DP的特殊性优化才是这题我要学习的地方。
-------------------------------------------------------------------------------------------------------------------
代码:
var
f:array[0..100,0..100,0..100] of double;
n,m:longint;
k1,k2:double;
function min_longint(a,b:longint):longint;
begin
if a<b then exit(a) else exit(b);
end;
function area(l,n:longint):double;
var
len:longint;
begin
if (l<n)or(n=0)then area:=1e9
else begin
len:=l div n;
area:=(n-1)*k2*sqr(len)+k2*sqr(l-len*(n-1))+k1*sqr(l);
end;
end;
procedure main;
var
l,p,q,j,k,len:longint;
x,y:double;
begin
readln(k1,k2,m,n);
for l:=0 to 100 do//init_f
for p:=0 to m do
for q:=0 to n do
f[l,p,q]:=-1;
f[0,0,0]:=0;
for l:=1 to 100 do//dp
for q:=1 to n do
for p:=1 to min_longint(q,m) do begin
f[l,p,q]:=1e9;
if p=1 then k:=q else k:=1;
for j:=1 to l-p+1 do begin
x:=f[l-j,p-1,q-k+1];
y:=area(j,k-1);
if (x<>-1)and(x+y<f[l,p,q])then f[l,p,q]:=x+y;
x:=f[l-j,p-1,q-k];
y:=area(j,k);
while (x<>-1)and(x+y<f[l,p,q]) do begin
f[l,p,q]:=x+y;
inc(k); if k>q then break;
x:=f[l-j,p-1,q-k];
y:=area(j,k);
end;
end;
end;
write(f[100,m,n]:0:1);
end;
begin
assign(input,'hut.in');reset(input);
assign(output,'hut.out');rewrite(output);
main;
close(input);close(output);
end.