10 ACwing 280 Jury Compromise 题解

Jury Compromise

题面

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

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

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

首先,参与诉讼的控方和辩方会给所有候选人打分,分值在 0 到 20 之间。

第 i 个人的得分分别记为 p[i] 和 d[i]。

为了公平起见,法官选出的 M 个人必须满足:辩方总分 D 和控方总分 P 的差的绝对值 |D−P| 最小。

如果选择方法不唯一,那么再从中选择辨控双方总分之和 D+P 最大的方案。

求最终的陪审团获得的辩方总分 D、控方总分 P,以及陪审团人选的编号。

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

\(1 \le N \le 200\)

\(1 \le M \le 20\)

\(0 \le p_i,d_i \le 20\)

题解

这是个典型的物品具有多价值的 01 背包问题,每个人有三个价值:对人数的贡献1,对 \(P\) 的贡献 \(p_i\) ,对 \(D\) 的贡献 \(d_i\)

所以我们可以设 \(f(j,d,p)\) 表示考虑前 \(i\) 个人的情况下,选了 \(j\) 个人,对 \(D\) 的贡献为 \(d\) ,对 \(P\) 的贡献为 \(p\) ,这样一个状态能否达到

答案就是我们在 \(f(M,d,p) = 1\) 里选保证 \(|d - p|\) 最小的情况下,\(d + p\) 最小的

这个做法的时间复杂度为 \(O(NM^5)\) ,大概6e8,包不行的

其实上面的做法并没有充分发挥 01背包 价值和体积的特性,它只解决了存在性问题,但并没有最小化,所以我们可以转化一下,将每个人的价值视为 \(d_i + p_i\) ,体积为 \(d_i - p_i\) ,设 \(f(i,j,k)\) 表示考虑前 \(i\) 个人,已经选了 \(j\) 个人,体积为 \(k\) 的最大价值

答案即为 \(f(n,M,k)\) ,满足 \(|k|\) 最小

code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>

using namespace std;

//如果下标可能为负数的话,那么我们要先定一个基准值,这样的话保证下标合法
const int N = 210, M = 810, base = 405;

int n, m;
int p[N], d[N], ans[N];
int f[N][25][M];

int main () {
    int Case = 0;
    while (true) {
        Case ++;
        cin >> n >> m;
        if (!n && !m) break;
        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 ++) {
            //这里 j 必须从 0 开始,为了继承一开始的状态
            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 P = 0, D = 0, 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]);
                    P += p[i];
                    D += d[i];
                    i --, j --;
                }
            }
        }
        printf ("Jury #%d\n", Case);
        printf ("Best jury has value %d for prosecution and value %d for defence:\n", P, D);
        if (cnt) reverse (ans + 1, ans + 1 + cnt);
        for (int i = 1; i <= cnt; i ++) {
            printf (" %d", ans[i]);
        }
        printf ("\n\n");
    }


    return 0;
}
posted @ 2025-10-05 17:59  michaele  阅读(6)  评论(0)    收藏  举报