[NOI-04]小H的小屋

小H的小屋

【问题描述】

H发誓要做21世纪最伟大的数学家。他认为,做数学家与做歌星一样,第一步要作好包装,不然本事再大也推不出去。为此他决定先在自己的住所上下功夫,让人一看就知道里面住着一个“未来的大数学家”。

为了描述方便,我们以向东为x轴正方向,向北为y轴正方向,建立平面直角坐标系。小H的小屋东西长为100HilHil是小H自己使用的长度单位,至于怎样折合成“m”,谁也不知道)。东墙和西墙均平行于y轴,北墙和南墙分别是斜率为k1和k2的直线,k1和k2为正实数。北墙和南墙的墙角处有很多块草坪,每块草坪都是一个矩形,矩形的每条边都平行于坐标轴。相邻两块草坪的接触点恰好在墙上,接触点的横坐标被称为它所在墙的“分点”,这些分点必须是199的整数。

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.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值