<<算法竞赛进阶指南>>:陪审团

在一个遥远的国家,一名嫌疑犯是否有罪需要由陪审团来决定。
陪审团是由法官从公民中挑选的。

法官先随机挑选 N N N 个人(编号 1 , 2 … , N 1,2…,N 1,2,N)作为陪审团的候选人,然后再从这 N N N 个人中按照下列方法选出 M M M 人组成陪审团。

首先,参与诉讼的控方和辩方会给所有候选人打分,分值在 0 0 0 20 20 20 之间。
i i i 个人的得分分别记为 p [ i ] p[i] p[i] d [ i ] d[i] d[i]。 为了公平起见,法官选出的 M M M 个人必须满足:辩方总分 D D D 和控方总分 P P P 的差的绝对值 ∣ D − P ∣ |D−P| DP 最小。
如果选择方法不唯一,那么再从中选择辨控双方总分之和 D + P D+P D+P 最大的方案。求最终的陪审团获得的辩方总分 D D D、控方总分 P P P,以及陪审团人选的编号。

注意:若陪审团的人选方案不唯一,则任意输出一组合法方案即可。

输入格式:

输入包含多组测试数据。
每组测试数据第一行包含两个整数 N N N M M M
接下来 N N N 行,每行包含两个整数 p [ i ] p[i] p[i] d [ i ] d[i] d[i]
每组测试数据之间隔一个空行。
当输入数据 N = 0 , M = 0 N=0,M=0 N=0M=0 时,表示结束输入,该数据无需处理。

输出格式:
对于每组数据,第一行输出 KaTeX parse error: Expected 'EOF', got '#' at position 6: Jury #̲C C C C
为数据编号,从 1 1 1 开始。
第二行输出 B e s t j u r y h a s v a l u e P f o r p r o s e c u t i o n a n d v a l u e D f o r d e f e n c e : Best jury has value P for prosecution and value D for defence: BestjuryhasvaluePforprosecutionandvalueDfordefence: P P P为控方总分, D D D 为辩方总分。
第三行输出按升序排列的陪审人选编号,每个编号前输出一个空格。
每组数据输出完后,输出一个空行。

数据范围:
1 ≤ N ≤ 200 1≤N≤200 1N200
1 ≤ M ≤ 20 1≤M≤20 1M20
0 ≤ p [ i ] , d [ i ] ≤ 20 0≤p[i],d[i]≤20 0p[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

首先 遇到背包问题, 确定背包类型, 每个人只能选一个, 故为 01 01 01 背包, 然后题目要求具体方案, 首先答案要求满足两个条件, 第一是要 ∣ D − P ∣ |D - P| DP 最小,其次是 D + P D + P D+P 最大, 我们可以将 D + P D + P D+P 定义为背包的属性

状态定义: f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]: 在前 i i i 个人中选, 总人数不超过 j j j, 并且总差值为 k k k 的集合
状态属性: D + P D + P D+P 的最大值
状态计算: 选第 i i i 个人 不选第 i i i 个人
f [ i ] [ j ] [ k ] = m a x ( 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[i1][j][k],f[i1][j1][k(p[i]d[i])]+p[i]+d[i]);

求出 D + P D + P D+P 的最大值并不难, 难点在于如何得到具体方案,我们可以通过方向搜一遍, 如果从终点状态,一直迭代到起始状态,能够进行状态转移,我们就记下这个方案

注意: 由于p[i] 是从 1 1 1 ~ 20 20 20, 所以 p [ i ] − d [ i ] p[i] - d[i] p[i]d[i] 是从 − 40 -40 40 ~ 40 40 40, 所以总的 p [ i ] − d [ i ] p[i] - d[i] p[i]d[i] 是从 − 400 -400 400 ~ 400 400 400, 但是如果直接使用,会导致数组越界,所以我们加上一个偏移量取名为 b a s e base base,其值 400 400 400, 故差值的范围就会变成 0 0 0 ~ 800 800 800

#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';
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

广西小蒟蒻

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值