水水的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.