zoukankan      html  css  js  c++  java
  • POJ 1015 / UVA 323 Jury Compromise(01背包,打印路径)

    POJ1015UVA323参考文章
    题目大意:给出(n)对数,从中选出(m)对数,使各对数的差累加和最小的情况下总和最大。
    (博主看了几篇文章,但是都没有考虑到背包容量的越界问题,关于这个,会在代码里解释。顺便提一下,POJ这题的数据是比较弱的,建议去UVA 323检测一下代码的正确性)
      设每对数的差值为(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
    Best jury has value %d for prosecution and value %d for defence:
    ", kase++, sumd, sump);
            for (int i = 0; i<m; ++i) printf(i==m-1 ? " %d
    
    " : " %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;
    }
    
  • 相关阅读:
    微信小程序-movable-view
    微信小程序-swiper组件
    大文件上传的php.ini配置和apache或者nginx需要的配置
    Yii2.0 实现关联查询
    Yii2.0关闭自带的debug功能
    创建日志文件,并且追加内容
    微信小程序实现计算器功能
    Yii2.0实现后台接收json数据处理数据修改功能
    Yii2.0成功或者失败提示➕页面跳转
    Yii2.0配置pathinfo模式
  • 原文地址:https://www.cnblogs.com/shuitiangong/p/12671664.html
Copyright © 2011-2022 走看看