码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2020-04-10 10:36:54      阅读:78      评论:0      收藏:0      [点我收藏+]

标签:rom   mem   过程   一个   参考   span   打印路径   val   math   

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;
}

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

标签:rom   mem   过程   一个   参考   span   打印路径   val   math   

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!