水水的DP,网上居然有人说200*400*20会爆内存~于是想了一个滚动数组的,但是状态记录就不好办了,后来才发现200*400*20是没问题的……
状态转移方程:
dp[i][j]表示选出i个人、两方差值为j的时候两方价值和的最大值
dp[i][j]=max{ dp[i-1][j-p[i]+d[i]]+p[i]+d[i] }
记录路径的话,直接记录并倒推即可……
参考代码:
program poj1015;//By_Thispoet
const
maxn=4000;
var
i,j,k,m,n,p,test :longint;
mini,maxi :longint;
f :array[0..200,-maxn..maxn]of longint;
a,b :array[0..maxn]of longint;
ans :array[0..20,-maxn..maxn,0..20]of longint;
function min(i,j:longint):longint;
begin
if i<j then exit(i);exit(j);
end;
function max(i,j:longint):longint;
begin
if i>j then exit(i);;exit(j);
end;
begin
readln(n,m);test:=0;
while not ((n=0)and(m=0)) do begin
inc(test);
for i:=1 to n do readln(a[i],b[i]);
mini:=0;maxi:=0;fillchar(f,sizeof(f),128);f[0,0]:=0;
for i:=1 to n do begin
p:=a[i]-b[i];
if p>=0 then begin
for j:=min(i,m) downto 1 do
for k:=maxi downto mini do
if f[j-1,k]>=0 then if f[j-1,k]+a[i]+b[i]>f[j,k+p] then begin
f[j,k+p]:=f[j-1,k]+a[i]+b[i];
move(ans[j-1,k,0],ans[j,k+p,0],4*j);ans[j,k+p,j]:=i;
end;
maxi:=maxi+p;
end else begin
for j:=min(i,m) downto 1 do
for k:=mini to maxi do
if f[j-1,k]>=0 then if f[j-1,k]+a[i]+b[i]>f[j,k+p] then begin
f[j,k+p]:=f[j-1,k]+a[i]+b[i];
move(ans[j-1,k,0],ans[j,k+p,0],4*j);ans[j,k+p,j]:=i;
end;
mini:=mini+p;
end;
end;
for i:=0 to max(maxi,-mini) do
if (f[m,i]>=0)and(f[m,i]>=f[m,-i]) then begin
j:=i;break;
end else if f[m,-i]>=0 then begin
j:=-i;break;
end;
writeln('Jury #',test);k:=j;
writeln('Best jury has value ',(f[m,j]+j)>>1,' for prosecution and value ',(f[m,j]-j)>>1,' for defence:');
for i:=1 to m do write(' ',ans[m,k,i]);writeln;writeln;
readln(n,m);
end;
end.