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