在一个遥远的国家,一名嫌疑犯是否有罪需要由陪审团来决定。
陪审团是由法官从公民中挑选的。
法官先随机挑选 NNN 个人(编号 1,2…,N1,2…,N1,2…,N)作为陪审团的候选人,然后再从这 NNN 个人中按照下列方法选出 MMM 人组成陪审团。
首先,参与诉讼的控方和辩方会给所有候选人打分,分值在 000 到 202020 之间。
第 iii 个人的得分分别记为 p[i]p[i]p[i] 和 d[i]d[i]d[i]。 为了公平起见,法官选出的 MMM 个人必须满足:辩方总分 DDD 和控方总分 PPP 的差的绝对值 ∣D−P∣|D−P|∣D−P∣ 最小。
如果选择方法不唯一,那么再从中选择辨控双方总分之和 D+PD+PD+P 最大的方案。求最终的陪审团获得的辩方总分 DDD、控方总分 PPP,以及陪审团人选的编号。
注意:若陪审团的人选方案不唯一,则任意输出一组合法方案即可。
输入格式:
输入包含多组测试数据。
每组测试数据第一行包含两个整数 NNN 和 MMM。
接下来 NNN 行,每行包含两个整数 p[i]p[i]p[i] 和 d[i]d[i]d[i]。
每组测试数据之间隔一个空行。
当输入数据 N=0,M=0N=0,M=0N=0,M=0 时,表示结束输入,该数据无需处理。
输出格式:
对于每组数据,第一行输出 KaTeX parse error: Expected 'EOF', got '#' at position 6: Jury #̲C,CCC
为数据编号,从 111 开始。
第二行输出 BestjuryhasvaluePforprosecutionandvalueDfordefence:Best jury has value P for prosecution and value D for defence:BestjuryhasvaluePforprosecutionandvalueDfordefence:,PPP为控方总分,DDD 为辩方总分。
第三行输出按升序排列的陪审人选编号,每个编号前输出一个空格。
每组数据输出完后,输出一个空行。
数据范围:
1≤N≤2001≤N≤2001≤N≤200
1≤M≤201≤M≤201≤M≤20
0≤p[i],d[i]≤200≤p[i],d[i]≤200≤p[i],d[i]≤20
输入样例:
4 2
1 2
2 3
4 1
6 2
0 0
输出样例:
Jury #1
Best jury has value 6 for prosecution and value 4 for defence:
2 3
首先 遇到背包问题, 确定背包类型, 每个人只能选一个, 故为 010101 背包, 然后题目要求具体方案, 首先答案要求满足两个条件, 第一是要 ∣D−P∣|D - P|∣D−P∣ 最小,其次是 D+PD + PD+P 最大, 我们可以将 D+PD + PD+P 定义为背包的属性
状态定义: f[i][j][k]f[i][j][k]f[i][j][k]: 在前 iii 个人中选, 总人数不超过 jjj, 并且总差值为 kkk 的集合
状态属性: D+PD + PD+P 的最大值
状态计算: 选第 iii 个人 不选第 iii 个人
f[i][j][k]=max(f[i−1][j][k],f[i−1][j−1][k−(p[i]−d[i])]+p[i]+d[i]);f[i][j][k] = max(f[i - 1][j][k], f[i - 1][j - 1][k - (p[i] - d[i])] + p[i] + d[i]);f[i][j][k]=max(f[i−1][j][k],f[i−1][j−1][k−(p[i]−d[i])]+p[i]+d[i]);
求出 D+PD + PD+P 的最大值并不难, 难点在于如何得到具体方案,我们可以通过方向搜一遍, 如果从终点状态,一直迭代到起始状态,能够进行状态转移,我们就记下这个方案
注意: 由于p[i] 是从 111 ~ 202020, 所以 p[i]−d[i]p[i] - d[i]p[i]−d[i] 是从 −40-40−40 ~ 404040, 所以总的 p[i]−d[i]p[i] - d[i]p[i]−d[i] 是从 −400-400−400 ~ 400400400, 但是如果直接使用,会导致数组越界,所以我们加上一个偏移量取名为 basebasebase,其值 400400400, 故差值的范围就会变成 000 ~ 800800800
#include<bits/stdc++.h>
using namespace std;
const int N = 210, M = 810, base = 400;
int f[N][21][M], p[N], d[N], ans[N], n, m;
int main()
{
int T = 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; //初始化为0
int cnt = 0; //初始化
for(int i = 1; i <= n; i ++) //枚举前i个人
for(int j = 0; j <= m; j ++) //枚举已经选了的人
for(int k = 0; k < M; k ++ ) //枚举差值 0~801
{
f[i][j][k] = f[i - 1][j][k]; //不选第 i 个人
int t = k - (p[i] - d[i]); //计算出差值,看是否符合题意
if(t < 0 || t > M ) continue;
if(j < 1) continue; //是否可以转移
f[i][j][k] = max(f[i][j][k], f[i - 1][j - 1][t] + p[i] + d[i]); //状态转移
}
int v = 0;
while( f[n][m][base + v] < 0 && f[n][m][base - v] < 0) v ++;
// 当v小于0和v大于0 都不合题意是, v++;
if(f[n][m][base + v] > f[n][m][base - v]) v = base + v;
else v = base - v;
//当v,-v都存在时,取最大值
int j = m, i = n, k = v;
while(j)
{
if(f[i][j][k] == f[i - 1][j][k]) i --;//如果可以不选第i个人
else
{
ans[cnt ++ ] = i; //选了第i个人
k -= p[i] - d[i]; //差值减小
i --, j --; //减小
}
}
int a = 0, b = 0;
for(int i = 0; i < cnt; i ++ ) a += p[ans[i]], b += d[ans[i]];
//记录答案
printf("Jury #%d\n", T++);
printf("Best jury has value %d for prosecution and value %d for defence:\n", a, b);
sort(ans, ans + cnt); //排序,保证编号是从小到大的
//打印方案
for(int i = 0; i < cnt; i ++ ) cout << ans[i] << ' ';
cout << '\n';
cout << '\n';
}
}
该博客讨论了一个关于陪审团选择的问题,其中涉及01背包问题和动态规划算法。问题要求选出一组人,使得辩方和控方的评分差值绝对值最小,并且总分之和最大。博主给出了详细的动态规划状态转移方程,以及如何通过回溯获取具体的选择方案。最后,博主提供了一个完整的C++代码实现来解决这个问题。
310

被折叠的 条评论
为什么被折叠?



