POJ 1015 / UVA 323 Jury Compromise(01背包,打印路径)

POJ1015?UVA323?参考文章
(博主看了几篇文章,但是都没有考虑到背包容量的越界问题,关于这个,会在代码里解释。顺便提一下,POj这题的数据是比较弱的,建议去UVA 323检测一下代码的正确性)
题目大意:给出$n$对数,从中选出$m$对数,使各对数的差累加和最小的情况下总和最大。
??设每对数的差值为$sub[i]$,和为$sum[i]$。要从中选出m个数,对于每个数来说,都有选与不选两种情况,所以能不能用背包来做呢?用背包的话还得确定容量。既然题目要求$sub$的总和最小的情况下$sum$最大,那么我们可以用差值$sub$来做容量,求每个差值下的最大的总和。
??但是这里一个问题,$sub$有可能是负的呀,为了解决这个问题我们可以让容量整体+400从$[-400,400]\(变成\)[0,800]$(为什么是这个数?因为最多选20对,每对最大差值20,最小-20),那么状态转移方程就是$dp[j][k] = dp[j-1][k-sub[i]]+sum[i]$($i$为物品编号,$j$为选出的第$j$个物品,$k$为容量。)
??然后题目还要求输出路径,这里博主水平有限,只会用$vector$来硬存每个差值下的路径。既然这一次的状态是由下一次的状态转移过来的,那么路径就是上一个状态选的物品再加上这一个状态选择的物品,如果从小到大的编号来$dp$的话,那么顺序自然就是升序的了。

//https://www.cnblogs.com/shuitiangong/
const int maxn = 2e2+10;
int n, m, kase = 1, sum[maxn], sub[maxn];
int dp[25][805];
vector<int> p[25][805];
int main(void) {
    while(~scanf("%d%d", &n, &m) && (n||m)) {
        for (int i = 1, d, p; i<=n; ++i) {
            scanf("%d%d", &d, &p);
            sum[i] = d+p; sub[i] = d-p;
        }
        //因为存在负数所以整体+400,以400为0并初始化0位dp的起点
        memset(dp, -1, sizeof(dp)); dp[0][400] = 0;
        for (int i = 1; i<=n; ++i)
            for (int j = m; j>=1; --j)
                for (int k = min(800, 800+sub[i]); k >= max(0, sub[i]); --k)
                //这里要说明一下k = min(800, 800+sub[i])是为了防止sub[i]为正的时候超出800
                //同样k >= max(0, sub[i])则是为了防止sub[i]为负的时候小于0
                //顺便再提一下k不管从高到低还是从低到高都行,因为有第一维的存在所以并没有后效性
                    if (dp[j-1][k-sub[i]]>=0 && dp[j][k] < dp[j-1][k-sub[i]]+sum[i]) {
                        dp[j][k] = dp[j-1][k-sub[i]]+sum[i];
                        p[j][k] = p[j-1][k-sub[i]];
                        p[j][k].push_back(i);
                    }
        int delta = 0;
        //从0(400)开始向两边枚举,求最小差值
        while(dp[m][400+delta]<0 && dp[m][400-delta]<0) ++delta;
	delta = dp[m][400+delta] >= dp[m][400-delta] ? 400+delta : 400-delta;
        int sumd = (dp[m][delta]+delta-400)/2;
        int sump = (dp[m][delta]-delta+400)/2;
        printf("Jury #%d\nBest jury has value %d for prosecution and value %d for defence:\n", kase++, sumd, sump);
        for (int i = 0; i<m; ++i) printf(i==m-1 ? " %d\n\n" : " %d", p[m][delta][i]);
        //因为p第0行始终是空的,所以其实在上面的dp过程中j=1的时候已经完成清空了,所以下面一段可以省略
        for (int i = 0; i<=20; ++i)
            for (int j = 0; j<=800; ++j)
                p[i][j].clear();
    }
    return 0;
}

原文地址:https://www.cnblogs.com/shuitiangong/p/12671664.html

时间: 2024-10-09 15:38:32

POJ 1015 / UVA 323 Jury Compromise(01背包,打印路径)的相关文章

0-1背包打印路径(递归和非递归版本)

简单的0-1背包打印路径问题,我们可以记录一个p[][]数组来判断,当前物品是否被选中,最后按照记录输出,注意是逆序. #include<stdio.h> #include<string.h> int main() { int a[25],p[25][10005],i,j,n,m,s[10005]; while(scanf("%d%d",&m,&n)!=EOF){ for(i=1;i<=n;i++) scanf("%d"

UVA 624 CD (01背包 带路径)

题意 输入两个数 len,n 表示长度和个数,接下来输入n个数, 表示每一个的长度, 求这n个数能够组成的不超过len的最大长度,并输出这些数. 分析:01背包,dp数组非0表示可以组成的数,dp数组用来记录路径 #include <iostream> #include <queue> #include <cstdio> #include <cstring> #include <cstdlib> #include <stack> #i

Uva 642-CD(0-1背包+打印路径)

题目链接:点击打开链接 裸01背包 ,此题中 价值即体积... 打印路径..不多说了 一维的没看懂..上个二维的 #include <algorithm> #include <iostream> #include <cstring> #include <cstdlib> #include <string> #include <cctype> #include <vector> #include <cstdio>

UVA 624 CD (01背包+打印路径 或 dfs+记录路径)

Description You have a long drive by car ahead. You have a tape recorder, but unfortunately your best music is on CDs. You need to have it on tapes so the problem to solve is: you have a tape N minutes long. How to choose tracks from CD to get most o

uva 624 CD 01背包打印路径

// 集训最终開始了.来到水题先 #include <cstdio> #include <cstring> #include <algorithm> #include <iostream> using namespace std; int a[23]; int d[23][100000]; int flag[23]; int W,n; void init(){ cin >> n; for (int i=1;i<=n;i++) cin >

UVA624(01背包记录路径)

题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=565 01背包打印路径. #include <cstdio> #include <cstring> using namespace std; const int MAXN=10005; int n,W; int w[30]; int dp[MAXN]; in

UVA 562 Dividing coins --01背包的变形

01背包的变形. 先算出硬币面值的总和,然后此题变成求背包容量为V=sum/2时,能装的最多的硬币,然后将剩余的面值和它相减取一个绝对值就是最小的差值. 代码: #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; #define N 50007 int c[102],d

UVA - 10163Storage Keepers(01背包)

题目大意:UVA - 10163Storage Keepers(01背包) 题目大意:现在有m个守店人,和n家店,每个守店人有个能力值,然后一个守护店的人可以守K家店,那么这些店能到的安全度就是Pi / K.店的安全度取决于守护它的人给的安全度中间最低的那个.这些店的最高安全度取决于最低安全度的那家店.现在问如何雇佣这些人使得店的安全度最高的情况下,费用最少. 解题思路: 安全度要求最高,那么就是要判断每个守店的人要不要雇佣,并且雇佣来之后让它守几家店(安全度).dp[i][j]表示:前面的i家

UVa 103 - Stacking Boxes (LIS,打印路径)

链接:UVa 103 题意:给n维图形,它们的边长是{d1,d2,d3...dn},  对于两个n维图形,求满足其中一个的所有边长 按照任意顺序都一一对应小于另一个的边长,这样的最长序列的个数,并且打印任意一个最长子串的路径, 例如:a(9,5,7,3),b(6,10,8,2),c(9,7,5,1),a和b不满足,但c和b满足 分析:首先对没组边长从小到大排序,再对各组图形按最小边排序,再求最大子串, 对于打印路径,可以逆序循环,也可递归求解 #include<cstdio> #include