陪审团(算法竞赛进阶指南)
在一个遥远的国家,一名嫌疑犯是否有罪需要由陪审团来决定。
陪审团是由法官从公民中挑选的。
法官先随机挑选 N 个人(编号 1,2…,N)作为陪审团的候选人,然后再从这 N 个人中按照下列方法选出 M 人组成陪审团。
首先,参与诉讼的控方和辩方会给所有候选人打分,分值在 0 到 20 之间。
第 i 个人的得分分别记为 p[i] 和 d[i]。
为了公平起见,法官选出的 M 个人必须满足:辩方总分 D 和控方总分 P 的差的绝对值 |D−P| 最小。
如果选择方法不唯一,那么再从中选择辨控双方总分之和 D+P 最大的方案。
求最终的陪审团获得的辩方总分 D、控方总分 P,以及陪审团人选的编号。
注意:若陪审团的人选方案不唯一,则任意输出一组合法方案即可。
输入格式
输入包含多组测试数据。
每组测试数据第一行包含两个整数 N 和 M。
接下来 N 行,每行包含两个整数 p[i] 和 d[i]。
每组测试数据之间隔一个空行。
当输入数据 N=0,M=0 时,表示结束输入,该数据无需处理。
输出格式
对于每组数据,第一行输出 Jury #C,C 为数据编号,从 1 开始。
第二行输出 Best jury has value P for prosecution and value D for defence:,P 为控方总分,D 为辩方总分。
第三行输出按升序排列的陪审人选编号,每个编号前输出一个空格。
每组数据输出完后,输出一个空行。
数据范围
1≤N≤200,
1≤M≤20
0≤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
状态确立:
状态确立需要考虑我们要维护哪些量,这个数据它有什么元素。在这道题中,变量有
1.目前已选的人数
2.目前的分数差
3.对于每一个人,他的分数不同,第i个人的i不同对结果也有影响,i也要参与维护
我们需要求的结果是当前分数差最小的分数最大的值。
虽然题目要求的是陪审团的人选方案,但本质仍然是求最大值。
f[i][j][k]表示从前i个人中找到j个人,分数差为k的最大分数值。
状态转移:
一、不选,则f[i][j][k]=f[i-1][j][k]。
二、选,f[i][j][k]=f[i-1][j-1][k-(p[i]-d[i])]+d[i]+p[i];
需要注意的事项:
1.数组下标不存在负数,但这道题的分数差可能是负数。要给整体加上一个向上的偏移量(20 * 20 = 400)。所以数组下标为0 - 800;
2.应该先判断是否能加入第i人,也就是说要先判断j减去1后会不会小于0。
判断答案:
找最小差值v。
我们先将v设为0(也就是最理想的情况)。
然后判断,方法是看f[n][m][v+base]和f[n][m][base-v]是否都小于0,(base为偏移量)
若都小于0,则说明不存在此种情况,则v++(我们初始化把f数组设为无穷大),利用while循环找出最小的v值。
此外可能出现base+v和base-v都合法的情况,则需要判断哪一个的总分最大(就是将f值比大小)
将v赋成那个方案所对应的v值(注意加上base)
得到方案:
方法是反过来推。首先从f[i][j][v](i=n,j=m,这里的v是上面找出来的最小差值加上base)开始,
判断他是由哪一种情况得来(选与不选),若是不选,直接将i--;若选,记录人选,将v减掉该人的差值,再i--,j--。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 210, M = 805, base = 400;
int n, m;
int p[N], d[N];
int f[N][21][M];
int ans[N];
int main()
{
int T = 1;
while (scanf("%d%d", &n, &m), n || m)
{
for (int i = 1; i <= n; i ++ ) scanf("%d%d", &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 < M; k ++ )
{
f[i][j][k] = f[i - 1][j][k];
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 ++ ;
if (f[n][m][base - v] > f[n][m][base + v]) v = base - v;
else v = base + v;
int cnt = 0;
int i = n, j = m, k = v;
while (j)
{
if (f[i][j][k] == f[i - 1][j][k]) i -- ;
else
{
ans[cnt ++ ] = i;
k -= (p[i] - d[i]);
i --, j -- ;
}
}
int sp = 0, sd = 0;
for (int i = 0; i < cnt; i ++ )
{
sp += p[ans[i]];
sd += d[ans[i]];
}
printf("Jury #%d\n", T ++ );
printf("Best jury has value %d for prosecution and value %d for defence:\n", sp, sd);
sort(ans, ans + cnt);
for (int i = 0; i < cnt; i ++ ) printf(" %d", ans[i]);
puts("\n");
}
return 0;
}