糖果
题目大意
你和你的朋友有一大包糖果,你想把这些糖按照能量尽可能平均分配,输入每种糖果的个数(<=500)与能量(<=200),把这些糖分成两部分使得两部分的能量尽可能接近。
个人想法
刚看到这道题,一脸懵逼。想了很久很久,打了一个贪心,感觉上是对的。因为时间关系,又没有打对拍。一直找不到错误样例。于是,只水了70分。(由此可得,对拍很重要)
正解
原来是DP。设f[i,j]表示前i种糖果,分给两边后差为J的情况是否成立。所以这f数组时boolean类型的。先快排一次,以糖果的能量为关键字,从大到小排。原因是如果先处理了小的会对后面答案造成影响,大的能量很难对区间造成小幅度的改变。再循环枚举,i是1到n,j只要开到0到200就够了。因为糖果能量最大是200,如果差值大于200,那么一定不是最优解。我们先判断f[i-1,j]是否成立(=true),如果为false,则f[i,j]在不选的情况下一定为false。如果成立,则往下做:先第三重循环0到第i种糖果的个数,abs(j+c[i]*k-c[i]*(s[i]-k))表示将k颗糖果分给多的那边,剩下的分给少的那边,反之abs(j-c[i]*k+c[i]*(s[i]-k)的意思则相反。我们就可以进行动态转移方程。(PS:当它的值不大于200时才可以执行)。
var
n,i,j,k:longint;
s,c:array[1..100]of longint;
f:array[0..100,0..200]of boolean;
procedure kp(l,r:longint);
var
i,j,mid,t:longint;
begin
i:=l;
j:=r;
mid:=c[(i+j) div 2];
repeat
while (c[i]>mid) do inc(i);
while (c[j]<mid) do dec(j);
if (i<=j) then
begin
t:=c[i];
c[i]:=c[j];
c[j]:=t;
t:=s[i];
s[i]:=s[j];
s[j]:=t;
inc(i);
dec(j);
end;
until i>j;
if (i<r) then kp(i,r);
if (l<j) then kp(l,j);
end;
begin
readln(n);
for i:=1 to n do
begin
readln(s[i],c[i]);
end;
kp(1,n);
fillchar(f,sizeof(f),false);
f[0,0]:=true;
for i:=1 to n do
begin
for j:=0 to 200 do
begin
if (f[i-1,j]) then
for k:=0 to s[i] do
begin
if (abs(j+c[i]*k-c[i]*(s[i]-k))<=200) then
f[i,abs(j+c[i]*k-c[i]*(s[i]-k))]:=true;//如果大于200,那肯定不是最优解
if (abs(j-c[i]*k+c[i]*(s[i]-k))<=200) then
f[i,abs(j-c[i]*k+c[i]*(s[i]-k))]:=true;
end;
end;
end;
for i:=0 to 200 do
if (f[n,i]) then
begin
writeln(i);
exit;
end;
end.
如果自己说什麽都做不到而什麽都不去做的话,那就更是什麽都做不到,什麽都不会改变,什麽都不会结束.