题意
为了审判某一个人,需要在n个人当中选出m个人组成陪审团,n个人中每个人都有作为起诉方的价值p和作为辩护方的价值d,为了保证公平性,要求m个人作为起诉方的价值之和P和作为辩护方的价值之和D满足 |P-D| 最小,在此基础上,要求P+D最大。最后求P,D以及选出的作为陪审团的m个人并且这m个人的字典序最小。
解题思路
看懂题目后,很容易就可以发现这是一个背包问题,即是否选择第i个人当作陪审团,不过所选人数必须是m个,相比于01背包问题,这个题目限制了选择的人数,那么我们就用dp[j][j] 表示当前选择了i个人,这i个人的P-D = j 的情况下,P+D的最大值,因为数组下标不能取负数,那我们加上一个修正值fix = 20 * m 即可
不过网上很多的写法都是有问题的,正确的解法应该这样枚举:选的人-->选的人作为第几个人-->P-D的值,枚举选的人的时候,我们从小开始枚举,这样一来就可以使得所选的人的字典序最小,记录路径的方法就用vector path[i][j] 记录对应dp[i][j]的情况下的所选的人
(其实这个应该算是比较常规的DP问题增加了一些套路,比如限制总数和记录路径)
代码区
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<queue> #include<string> #include<fstream> #include<vector> #include<stack> #include <map> #include <iomanip> #define bug cout << "**********" << endl #define show(x, y) cout<<"["<<x<<","<<y<<"] " #define LOCAL = 1; using namespace std; typedef long long ll; const ll inf = 1e18 + 7; const ll mod = 1e9 + 7; const int Max = 2e2 + 10; int n, m; int sub[Max], add[Max]; int dp[25][805]; vector<int> path[25][805]; int main() { #ifdef LOCAL // freopen("input.txt", "r", stdin); // freopen("output.txt", "w", stdout); #endif int kCase = 0; while (scanf("%d%d", &n, &m) != EOF && n + m) { for (int i = 1, p, d; i <= n; i++) scanf("%d%d", &p, &d), sub[i] = p - d, add[i] = p + d; int fix = 20 * m; for (int i = 0; i <= m; i++) for (int j = 0; j <= 2 * fix; j++) path[i][j].clear(), dp[i][j] = -1; dp[0][fix] = 0; for (int i = 1; i <= n; i++) for (int j = m - 1; j >= 0; j--) for (int k = 0; k <= 2 * fix; k++) if (dp[j][k] >= 0 && 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 dis; for (dis = 0; dis <= fix; dis++) { if (dp[m][fix - dis] >= 0 || dp[m][fix + dis] >= 0) break; } int S = dp[m][fix - dis] > dp[m][fix + dis] ? fix - dis : fix + dis; int P = (dp[m][S] + S - fix) / 2; int D = (dp[m][S] - S + fix) / 2; printf("Jury #%d ", ++kCase); printf("Best jury has value %d for prosecution and value %d for defence: ", P, D); for (int i = 0; i < m; i++) { printf("%d%c", path[m][S][i], i == path[m][S].size() - 1 ? ' ' : ' '); } printf(" "); } return 0; }