1 条题解
-
1
每日一题 #041 2022.10.7
Statement
选一个陪审团,先随机挑选 个人作为陪审团的候选人,然后选 人组成陪审团。控方和辩方会给候选人打分。请做到选出的 个人满足辩方总分和控方总分的差的绝对值最小。如果有多种选择方案,那么选辩控双方总分之和最大的方案。如果方案还是不惟一,输出任意一种均可。
- 任何分值不超过 。
Tutorial
Click to see
虽然数据范围不大,以为是搜索,但其实是受了“宇宙射线”影响的超级变形背包。可能连一点背包的影子都不见了。
考虑 DP。设状态 表示前 个人中选 个人,得分差为 ,最大的得分总和。注意这里得分差不是绝对值差,为了防止下标出现负数,差需要加上 。
这道题难于找出答案的具体方案。在 DP 过程中,把所有不合法解全部标为负无穷,从 处剖开,不难发现,最终状态中找到的第一个合法状态,就是我们想要的。后面的倒退不难,注意输出格式。
为什么这样就解决问题了呢?要求差最小,把 看作 ,也就是对于 ,如果状态 合法, 的绝对值一定是最小的。至于和最大,DP 迭代过程中已经计算出来了。
// 陪审团 #include <iostream> #include <cstring> #include <vector> #include <cstdio> #include <algorithm> #define SIZE 205 #define all(x) x.begin(), x.end() #define debug(x) cout<<#x<<":"<<x<<endl; using namespace std; int n, m; int p[SIZE], d[SIZE]; int base=400; int f[SIZE][21][1000]; signed main() { int COUNT=1; while(cin>>n>>m && n+m) { for(int i=1; i<=n; i++) cin>>p[i]>>d[i]; memset(f, -0x3f, sizeof(f)); f[0][0][base]=0; for(int i=1; i<=n; i++) for(int j=0; j<=m; j++) for(int k=0; k<1000; k++) { int tmp=k-(p[i]-d[i]); if(tmp<0 || tmp>=1000) { f[i][j][k]=f[i-1][j][k]; continue; } if(!j) { f[i][j][k]=f[i-1][j][k]; continue; } f[i][j][k]=max(f[i-1][j][k], f[i-1][j-1][tmp]+p[i]+d[i]); } int v=0; while(f[n][m][base-v]<0 && f[n][m][base+v]<0) v++; if(f[n][m][base-v]>f[n][m][base+v]) v=base-v; else v=base+v; vector<int> ans; int i=n, j=m, k=v; while(j) { if(f[i][j][k]==f[i-1][j][k]) i--; else { ans.push_back(i); k-=(p[i]-d[i]); i--, j--; } } int sp=0, sd=0; for(int i=0; i<ans.size(); i++) sp+=p[ans[i]], sd+=d[ans[i]]; printf("Jury #%d\n", COUNT++); printf("Best jury has value %d for prosecution and value %d for defence:\n", sp, sd); sort(all(ans)); for(int i=0; i<ans.size(); i++) cout<<" "<<ans[i]; cout<<endl; cout<<endl; } return 0; }
- 1
信息
- ID
- 16
- 时间
- 1000ms
- 内存
- 64MiB
- 难度
- 8
- 标签
- (无)
- 递交数
- 18
- 已通过
- 7
- 上传者