POJ 1015 Jury Compromise

POJ 1015 Jury Compromise

题意:

\(n\) 对数字,要从 \(n\) 对数字中选出 \(m\) 对,目标是使得第一组的数字和与第二组的数字和的差的绝对值最小。基于差的绝对值最小的基础上,两组数的总和最大,输出两组最后的分数,以及选法。

思路:
类似 \(01\) 背包问题,并且需要输出方案。

因为选出 \(m\) 对,所以肯定有两个维度 \(f[i][j]\) : 遍历到第 \(i\) 个物品,选了 \(j\) 个人,然后我们目的是使得这个绝对值差最小,通过分析数据我们可以知道,两者的差最多去到 \(800\) ,所以考虑第三维度直接记录两者的差值。因为出现负数,所以这里用 \(basic = 801\) 来表示差值为 \(0\)

定义 \(f[i][j][k]\) 为遍历到第 \(i\) 个人,已经选了 \(j\) 个人,当前两组的差值为 \(k\) 时的总和最大值。

然后每对数字分选和不选两种情况。就是 \(01\) 背包问题。

最后,遍历一次来找出最终的答案即可。

本题的初始化,只有当选了之后才有,所以这里直接从第一组开始初始化(注意初始化 \(f[1][1][]\) 之后 \(f[2][1][]\) 也需要 \(f[1][1][]\)

这道题细节比较多,比如说每个案例输出一行,输出的选法前面有个空格(当然还是我太菜写太丑的原因)

实现:

#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 205, M = 1650, INF = -1e9, base = 801;
int a[N], b[N];
int f[N][25][M];

int main()
{
    int n, m;
    int lun = 1;
    while (scanf("%d%d", &n, &m) != EOF)
    {
        if (!n && !m)
            break;

        for (int i = 1; i <= n; i++)
            scanf("%d%d", &a[i], &b[i]);

        //初始化1
        for (int i = 0; i < N; i++)
            for (int j = 0; j < 25; j++)
                for (int k = 0; k < M; k++)
                    f[i][j][k] = INF;

        //初始化2
        for (int i = 1; i <= n; i++)
            f[i][1][a[i] - b[i] + base] = a[i] + b[i];

        for (int i = 1; i <= n; i++)
        {
            //注意这里要取 min(i,m)
            for (int j = 1; j <= min(i, m); j++)
            {
                for (int k = 0; k < M; k++)
                {
                    //当前不选
                    if (f[i - 1][j][k] != INF)
                        f[i][j][k] = max(f[i][j][k], f[i - 1][j][k]);

                    //当前选
                    int sum = k - a[i] + b[i];
                    if (sum >= 0 && sum < M && f[i - 1][j - 1][sum] != INF)
                    {
                        f[i][j][k] = max(f[i][j][k], f[i - 1][j - 1][sum] + a[i] + b[i]);
                    }
                }
            }
        }

        int re = 0, redif = -INF, fl;
        for (int i = 0; i < M; i++)
        {
            if (f[n][m][i] != INF)
            {
                if (abs(i - base) < redif)
                {
                    redif = abs(i - base);
                    re = f[n][m][i];
                    fl = i;
                }
                else if (abs(i - base) == redif && f[n][m][i] >= re)
                {
                    re = f[n][m][i];
                    fl = i;
                }
            }
        }

        int rex = (redif + re) / 2;
        int rey = re - rex;
        //上面的计算方法,求出来的rex >= rey所以还需要根据 fl 对应的正负来判断哪个对应哪个
        if (fl < 801)
            swap(rex, rey);

        printf("Jury #%d \n", lun++);
        printf("Best jury has value %d for prosecution and value %d for defence: \n", rex, re - rex);
        vector<int> peo;
        int j = m, k = re;
        for (int i = n; i >= 1; i--)
        {
            if (j != 1)
            {
                if (fl - (a[i] - b[i]) >= 0 && fl - (a[i] - b[i]) < M && k == f[i - 1][j - 1][fl - (a[i] - b[i])] + (a[i] + b[i]))
                {
                    peo.push_back(i);
                    j--;
                    fl -= a[i] - b[i];
                    k -= a[i] + b[i];
                }
            }
            else if (j == 1)
            {
                if (fl - (a[i] - b[i]) >= 0 && fl - (a[i] - b[i]) < M && k == a[i] + b[i])
                {
                    peo.push_back(i);
                    j--;
                    fl -= a[i] - b[i];
                    k -= a[i] + b[i];
                    break;
                }
            }
        }
        for (int i = peo.size() - 1; i >= 0; i--)
        {
            printf(" %d", peo[i]);
        }
        printf("\n\n");
    }
    return 0;
}
posted @ 2022-12-23 21:56  zxr000  阅读(24)  评论(0编辑  收藏  举报