zoukankan      html  css  js  c++  java
  • hdu 2126 Buy the souvenirs 【输出方案数】【01背包】(经典)

    题目链接:https://vjudge.net/contest/103424#problem/K

    转载于:https://blog.csdn.net/acm_davidcn/article/details/5549933

    题目大意:

    给n个物品,和m块钱,输出能够购买最多物品的个数和购买这么多物品的方案数。

     解题分析:

    背包的一种进化版 , 除了记录最多能买多少个 , 需要记录买这么多个的方法 , 所以要在二维的基础上加多一维 .

    状态转移方程如下 : f[i][j][k]=f[i-1][j-t[i]][k-1]+f[i-1][j][k];

    ( 前 i 个物品在有 j 元的时候买 k 个物品的方法 ,t[i] 为第 i 个物品的价格 )

    #include <cstdio>
    #include <iostream>  
    #include <algorithm>  
    using namespace std;
    int val[35], dp[35][35][505];                      //dp[i][j][k]表示有k块钱,在前i种物品买j个物品的数量  
    int main() {
        int i, j, k, t, n, m, sign;
        scanf("%d", &t);
        while (t--) {
            scanf("%d%d", &n, &m);
            for (i = 1; i <= n; i++)
                scanf("%d", &val[i]);
            memset(dp, 0, sizeof(dp));
            for (i = 0; i <= n; i++)
                for (k = 0; k <= m; k++)
                    dp[i][0][k] = 1;                          //初始化,一样不买种类是1  
            for (i = 1; i <= n; i++) {
                for (j = 1; j <= i; j++)
                    for (k = m; k >= 0; k--) {
                        if (k >= val[i])
                            dp[i][j][k] += dp[i - 1][j - 1][k - val[i]] + dp[i - 1][j][k];     //加上买和不买的情况
                    }
            }
            sign = 0;
            for (i = n; i >= 1; i--) {
                if (dp[n][i][m] != 0) {          //找购买方案数不为0的最大购买数量i
                    printf("You have %d selection(s) to buy with %d kind(s) of souvenirs.
    ", dp[n][i][m], i);
                    sign = 1;
                    break;
                }
            }
            if (!sign)
                puts("Sorry, you can't buy anything.");
        }
        return 0;
    }

    第二种方法,对上述代码进行优化

    dp[i][j][0]表示前i个物品有j元时的最多物品数,dp[i][j][1]用来储存方案数

    ***设当前所选的物品为i
    1.  若选了物品i后,能买的件数比不选物品i的件数大,即dp[j - val[i]]>dp[j]
    那么更新dp[j],同时,dp[j][1]的方案数即为dp[j - val[i]][1]
    原因是:假设dp[j - val[i]]的方案数为 AB AC 两种,那么在此情况下加个D,为ABD, ACD,仍为两种,所以dp[j] = f[j - val[i]]即可
    当然,要注意dp[j - val[i]]为0的情况,因此当它为0时,dp[j] = 1,1即为D

    2.  若选了物品i后,能买的件数比不选物品i的件数相同,即dp[j - val[i]] == dp[j]
    即原先不选第i个物品,所需要的方案数为dp[j];而选了物品i的方案数为dp[j - val[i]]。
    因此,总的方案数即为dp[j] + dp[j - val[i]]
    当然,这里也要注意dp[j - val[i]] = 0的情况,当它为0时,dp[j] += 1,1即为D

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>  
    #define mem(p,k) memset(p,k,sizeof(p)) 
    using namespace std;
    int p[510], dp[510][2];          //dp[i][j][0]表示前i个物品,最多有j元,能买多少种物品
    int main() {                     //dp[i][j][1]表示前i个物品,最多有j元,能买最多物品的方案数
        int t, cur = 1;
        cin >> t;
        while (t--) {
            int m, n;
            scanf("%d%d", &n, &m);
            for (int i = 1; i <= n; i++)scanf("%d", p + i);
            mem(dp, 0);
            for (int i = 0; i <= m; i++)dp[i][1] = 1;              //初始化,钱数为j,一个物品都不买的方案数为1(因为此时对应的dp[i][0]=0,表示一个物品都没买)
            for (int i = 1; i <= n; i++) {
                for (int j = m; j >= p[i]; j--) {
                    if (dp[j][0] == dp[j - p[i]][0] + 1) {         //若选了物品i后,能买的件数比不选物品i的件数相同
                        if (!dp[j - p[i]][1])dp[j][1] += 1;
                        else
                            dp[j][1] += dp[j - p[i]][1];
                    }
                    else if(dp[j][0]<dp[j - p[i]][0] + 1) {        //若选了物品i后,能买的件数比不选物品i的件数大
                        dp[j][0] = dp[j - p[i]][0] + 1;
                        if (!dp[j - p[i]][1])dp[j][1] = 1;
                        else
                            dp[j][1] = dp[j - p[i]][1];
                    }
                }
            }
            if (dp[m][0]) {
                printf("You have %d selection(s) to buy with %d kind(s) of souvenirs.
    ", dp[m][1], dp[m][0]);
            }
            else printf("Sorry, you can't buy anything.
    ");
        }
    }

     2018-05-21

  • 相关阅读:
    C#计算两个日期之间相差的天数
    js字符串转时间
    mysql find_in_set 函数
    css控制网页所有图片当图片大于指定宽度图片等于指定宽度
    网页自动适应手机屏幕宽度的方法
    c# 生成指定范围的数字和字母组合随机数
    SqlServer 查看被锁的表和解除被锁的表
    C#隐情信息(银行账户,身份证号码,名字)中间部分特殊字符替换(*)
    c#检查SQL语法是否正确,不执行SQL语句
    sqlserver中的表值函数和标量值函数
  • 原文地址:https://www.cnblogs.com/00isok/p/9069929.html
Copyright © 2011-2022 走看看