陪审团(算法竞赛进阶指南)

详见AcWing 280. 陪审团 - AcWing

在一个遥远的国家,一名嫌疑犯是否有罪需要由陪审团来决定。

陪审团是由法官从公民中挑选的。

法官先随机挑选 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;
}

posted @ 2022-09-24 21:16  zyc_xianyu  阅读(90)  评论(0编辑  收藏  举报