版权声明:转载请注明出处 https://blog.csdn.net/jay__bryant/article/details/82023753
题意:给定人数n(1<=n<=200),每个人有一个a, b。(0<=a<=b),从中选择m个人,使得|sigma(a)-sigma(b)|最小,若相等则sigma(a)+sigma(b)最大。求最优策略的sigma(a),sigma(b)和选择的人的编号
dp[i][j]:选择了i个人,|sigma(a)-sigma(b)|==j时sigma(a)+sigma(b)最大为dp[i][j]。
由于|sigma(a)-sigma(b)|可能有负值,且最大负值为-20*m,所以令0 -> -20*m, 20*m->0, 2*20*m->20*m。
在状态转移的时候更新路径即可。
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
const int N = 210;
const int INF = 0x3f3f3f3f;
int dp[N][N<<2];
vector <int> path[N][N<<2];
int add[N],sub[N];
int n, m;
int main()
{
int cas = 0;
while(~scanf("%d %d", &n, &m) && (m+n))
{
for(int i = 1; i <= m; ++i)
for(int j = 0; j < N; ++j)
path[i][j].clear();
for(int i = 1; i <= n; ++i)
{
int a, b;
scanf("%d %d", &a, &b);
add[i] = a+b;
sub[i] = a-b;
}
memset(dp, -1, sizeof(dp));
int zero = 20*m;
dp[0][zero] = 0;
for(int i = 1; i <= n; ++i)
for(int j = m-1; j>=0; --j)
for(int k = 2*zero; k>0 ; --k)
if(~dp[j][k] && (k+sub[i]>=0&&k+sub[i]<=2*zero))
if(dp[j][k]+add[i] > dp[j+1][k+sub[i]])
{
dp[j+1][k+sub[i]] = dp[j][k]+add[i];
path[j+1][k+sub[i]] = path[j][k];
path[j+1][k+sub[i]].push_back(i);
}
int i = 0;
while(dp[m][i+zero]==-1 && dp[m][-i+zero]==-1) ++i;
int tmp = dp[m][i+zero]>dp[m][-i+zero]?i+zero:-i+zero;
int ans1 = (dp[m][tmp]+(tmp-zero))/2;
int ans2 = (dp[m][tmp]-(tmp-zero))/2;
printf("Jury #%d\n", ++cas);
printf("Best jury has value %d for prosecution and value %d for defence:\n", ans1, ans2);
for(int i = 0; i < m; ++i)
printf(" %d", path[m][tmp][i]);
printf("\n\n");
}
return 0;
}