【dp每日一题】POJ 1015 Jury Compromise
大意:
n个候选人,从中选出m个人。
控方和辩方会根据对候选人的喜欢程度,给所有候选人打分,分值从0到20。选出的m个人,必须满足辩方总分和控方总分的差的绝对值最小。如果有多种选择方案的辩方总分和控方总分的之差的绝对值相同,那么选辩控双方总分之和最大的方案即可。
思路:
\(dp[i][k]\)代表选了i个人且控辩差为k的情况下,控辩和的最大值。
为了处理方便,需要将k都加上m*20,这样保证所有的k都是大于等于0的。
初始将dp数组赋值为-1,\(dp[0][m*20]=0\),只有当dp数组不等于-1时可以通过这个状态转移,同时需要保证要选的人之前没有选过,那么只需要维护一个pre数组记录达到当前状态选了哪些人即可
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
#include <math.h>
#include <cstdio>
using namespace std;
const int N = 200 + 5;
typedef long long LL;
int n, m, a[N], b[N], dp[25][1000], maxk, pre[25][1000], res[25];
int cases = 0;
int main() {
while (cin >> n >> m && (n + m != 0)) {
cases++;
memset(dp, -1, sizeof dp);
memset(pre, -1, sizeof pre);
for (int i = 0; i < n; i++) {
cin >> a[i] >> b[i];
}
maxk = m * 20;
dp[0][maxk] = 0;
for (int i = 0; i < m; i++)
for (int k = 0; k <= maxk * 2; k++)
if (dp[i][k] != -1)
for (int j = 0; j < n; j++) {
if (a[j] + b[j] + dp[i][k] >
dp[i + 1][k + a[j] - b[j]]) {
int x = i, y = k;
while (x > 0 && pre[x][y] != j) {
y = y - (a[pre[x][y]] - b[pre[x][y]]);
x--;
}
if (x == 0) { // j不在路径内
pre[i + 1][k + a[j] - b[j]] = j;
dp[i + 1][k + a[j] - b[j]] =
a[j] + b[j] + dp[i][k];
}
}
}
int k = 0;
for (k = 0; k <= maxk; k++) {
if (dp[m][k + maxk] != -1) break;
if (dp[m][-k + maxk] != -1) break;
}
if (dp[m][k + maxk] > dp[m][-k + maxk])
k = k + maxk;
else
k = maxk - k;
cout << "Jury #" << cases << endl;
cout << "Best jury has value " << (k - maxk + dp[m][k]) / 2
<< " for prosecution and value " << (dp[m][k] - k + maxk) / 2
<< " for defence: " << endl;
for (int i = m; i > 0; i--) {
res[i-1] = pre[i][k]+1;
k = k - (a[pre[i][k]] - b[pre[i][k]]);
}
sort(res, res + m);
for (int i = 0; i < m; i++) cout << ' ' << res[i];
cout << endl;
}
return 0;
}