这道题是本人在训练中发现的,具有挺高的教学意义,因此做一下分析。
简化一下题目大意,是这样的:在一个二维平面上给出若干个点,你可以划出一个无限宽,但是与X轴的夹角为Alpha度的区间,此区间的收益为区间所覆盖的点的(分数和*倍数和/点数),求最大的收益和。(区间不能重叠)
乍一看,似乎无从下手,甚至想出了按斜率排序的处理。但是,仔细看一下,发现:如果从每个点出发做一条直线,且直线同时交X轴Alpha度,就能得到一个交点,如果选取的区间的边界覆盖了这个交点,不就刚好能被选到了吗?
于是乎,新的想法出炉了:
但是,新的问题又来了!怎么求落在X轴上的点呢?
我们都知道一次函数的公式:(Y=KX+B),
我们有点(x1,y1)和点(0,y2),Y=K*X1+B,0=K*X2+B, 移项得:
-B=K*X2,X2=-B/K! 也就是说,只要得出B和K,就能得出落在X轴上点的位置。K可以用正切函数得到,K=Tan(Alpha*π/180),B也就等于(Y-K*X1)。很轻松地得出X2。
求出落点后,判断下有没有落点重复的,合并(一个落点必须同时选或不选)。再进行DP就完成了。
CODE(由于楼主很懒,没有时间翻译成C,所以只贴上一段P)
uses math;
var
x,y:array[1..2000]of longint;
value,mul,sum1,sum2:array[0..2000]of longint;
i,j,l,n,m,a,last:longint; k,b:real;
poi:array[0..2000]of real;
f:array[0..2000]of real;
bz:array[1..2000]of boolean;
procedure sort(l,r:longint);
var
i,j:longint; mid:real;
begin
i:=l;j:=r;
mid:=poi[(i+j) div 2];
repeat
while poi[i]<mid do inc(i);
while poi[j]>mid do dec(j);
if i<=j then
begin
value[0]:=value[i];value[i]:=value[j];value[j]:=value[0];
poi[0]:=poi[i];poi[i]:=poi[j];poi[j]:=poi[0];
mul[0]:=mul[i];mul[i]:=mul[j];mul[j]:=mul[0];
inc(i);dec(j);
end;
until i>j;
if i<r then sort(i,r);
if j>l then sort(l,j);
end;
begin
readln(n);
for i:=1 to n do
readln(x[i],y[i],value[i],mul[i]);
readln(a);
k:=tan(a*3.1415926/180);
for i:=1 to n do
begin
b:=y[i]-k*x[i];
poi[i]:=b;
end;
sort(1,n);
last:=1;
for i:=2 to n do
begin
if (poi[i]=poi[last]) then
begin
inc(value[last],value[i]);
inc(mul[last],mul[i]);
value[i]:=0;
mul[i]:=0;
bz[i]:=true;
end else
last:=i;
end;
sum1[1]:=value[1];
sum2[1]:=mul[1];
for i:=2 to n do
begin
sum1[i]:=sum1[i-1]+value[i];
sum2[i]:=sum2[i-1]+mul[i];
end;
for i:=1 to n do
for j:=1 to i do
f[i]:=max(f[i],f[j-1]+(sum1[i]-sum1[j-1])*(sum2[i]-sum2[j-1])/(i-j+1));
writeln(f[n]:0:3);
end.