POJ 1015 Jury Compromise (记录路径的背包问题)

(点击此处查看原题)

题意

为了审判某一个人,需要在n个人当中选出m个人组成陪审团,n个人中每个人都有作为起诉方的价值p和作为辩护方的价值d,为了保证公平性,要求m个人作为起诉方的价值之和P和作为辩护方的价值之和D满足 |P-D| 最小,在此基础上,要求P+D最大。最后求P,D以及选出的作为陪审团的m个人并且这m个人的字典序最小。

解题思路

看懂题目后,很容易就可以发现这是一个背包问题,即是否选择第i个人当作陪审团,不过所选人数必须是m个,相比于01背包问题,这个题目限制了选择的人数,那么我们就用dp[j][j] 表示当前选择了i个人,这i个人的P-D = j 的情况下,P+D的最大值,因为数组下标不能取负数,那我们加上一个修正值fix = 20 * m 即可

不过网上很多的写法都是有问题的,正确的解法应该这样枚举:选的人-->选的人作为第几个人-->P-D的值,枚举选的人的时候,我们从小开始枚举,这样一来就可以使得所选的人的字典序最小,记录路径的方法就用vector path[i][j] 记录对应dp[i][j]的情况下的所选的人

(其实这个应该算是比较常规的DP问题增加了一些套路,比如限制总数和记录路径)

代码区

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<string>
#include<fstream>
#include<vector>
#include<stack>
#include <map>
#include <iomanip>

#define bug cout << "**********" << endl
#define show(x, y) cout<<"["<<x<<","<<y<<"] "
#define LOCAL = 1;
using namespace std;
typedef long long ll;
const ll inf = 1e18 + 7;
const ll mod = 1e9 + 7;
const int Max = 2e2 + 10;

int n, m;
int sub[Max], add[Max];
int dp[25][805];
vector<int> path[25][805];

int main()
{
#ifdef LOCAL
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
#endif
    int kCase = 0;
    while (scanf("%d%d", &n, &m) != EOF && n + m)
    {
        for (int i = 1, p, d; i <= n; i++)
            scanf("%d%d", &p, &d), sub[i] = p - d, add[i] = p + d;

        int fix = 20 * m;

        for (int i = 0; i <= m; i++)
            for (int j = 0; j <= 2 * fix; j++)
                path[i][j].clear(), dp[i][j] = -1;
        dp[0][fix] = 0;

        for (int i = 1; i <= n; i++)
            for (int j = m - 1; j >= 0; j--)
                for (int k = 0; k <= 2 * fix; k++)
                    if (dp[j][k] >= 0 && dp[j][k] + add[i] > dp[j + 1][k + sub[i]])
                    {
                        dp[j + 1][k + sub[i]] = dp[j][k] + add[i];
                        path[j + 1][k + sub[i]] = path[j][k];
                        path[j + 1][k + sub[i]].push_back(i);
                    }
        int dis;
        for (dis = 0; dis <= fix; dis++)
        {
            if (dp[m][fix - dis] >= 0 || dp[m][fix + dis] >= 0)
                break;
        }
        int S = dp[m][fix - dis] > dp[m][fix + dis] ? fix - dis : fix + dis;
        int P = (dp[m][S] + S - fix) / 2;
        int D = (dp[m][S] - S + fix) / 2;

        printf("Jury #%d\n", ++kCase);
        printf("Best jury has value %d for prosecution and value %d for defence:\n", P, D);
        for (int i = 0; i < m; i++)
        {
            printf("%d%c", path[m][S][i], i == path[m][S].size() - 1 ? '\n' : ' ');
        }
        printf("\n");
    }
    return 0;
}
View Code
posted @ 2019-09-23 20:00  winter-bamboo  阅读(251)  评论(0编辑  收藏  举报