PKU 1015-Jury Compromise

問題概要

翻訳Wikiを参照

解法

動的計画法
二つの重さの差といくつ取ったかでDP.
現在の最大の重さをメモする.
計算量は800x20x200ぐらい

実装(C++)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
int n,m;
int a[200],b[200];
short dp_data[21][201][1000];
unsigned char back_data[21][201][1000];
short *dp[21][201];
unsigned char *back[21][201];
int testcase=1;
int main() {
	while(scanf("%d%d",&n,&m),(n|m)){
		for(int i=0;i<n;i++){
			scanf("%d%d",a+i,b+i);
		}
		memset(dp_data,-1,sizeof(dp_data));
		memset(back_data,-1,sizeof(back_data));
		for(int i=0;i<21;i++){
			for(int j=0;j<201;j++){
				dp[i][j]=dp_data[i][j]+500;
				back[i][j]=back_data[i][j]+500;
			}
		}
		dp[0][0][0]=0;
		for(int k=0;k<n;k++){
			for(int i=-420;i<=420;i++){
				for(int j=m;j>=0;j--){
					if(dp[j][k][i]>=0){
						if(dp[j][k+1][i]<dp[j][k][i]){
							dp[j][k+1][i]=dp[j][k][i];
							back[j][k+1][i]=back[j][k][i];
						}
						if(j==m)continue;
						if(dp[j+1][k+1][i+a[k]-b[k]]<dp[j][k][i]+a[k]){
							dp[j+1][k+1][i+a[k]-b[k]]=dp[j][k][i]+a[k];
							back[j+1][k+1][i+a[k]-b[k]]=k;
						}
					}
				}
			}
		}
		for(int i=0;i<610;i++){
			int MAX=-1,R=-1;
			if(dp[m][n][-i]!=-1){
				MAX=max(MAX,dp[m][n][-i]+dp[m][n][-i]+i);
				R=-1;
			}
			if(dp[m][n][i]!=-1){
				if(MAX<dp[m][n][i]+dp[m][n][i]+i){
					MAX=dp[m][n][i]+dp[m][n][i]+i;
					R=1;
				}
			}
			if(MAX!=-1){
				int sum=i*R,now=n;
				vector<int> ans;
					for(int x=m;x>0;x--){
						ans.push_back(back[x][now][sum]);
						int t=back[x][now][sum];
						sum-=a[back[x][now][sum]]-b[back[x][now][sum]];
						now=t;
					}
				cout<<"Jury #"<<testcase++<<endl;
				cout<<"Best jury has value ";
				cout<<dp[m][n][i*R];
				cout<<" for prosecution and value ";
				cout<<dp[m][n][i*R]-i*R;
				cout<<" for defence:"<<endl;
				std::sort(ans.begin(),ans.end());
				for(int i=0;i<(int)ans.size();i++){
					cout<<" "<<ans[i]+1;
				}
				cout<<endl<<endl;
				break;
			}
		}
	}

	return 0;
}