zoukankan      html  css  js  c++  java
  • HDU

    题目链接
    题目大意:给定几种不同面额的硬币若干枚,需要求的用这些硬币可以组成多少种范围在1~m的不同面额的组合。
      题面是很典型的多重背包,但是数据看起来并不是直接用多重背包就能过的。这里需要多重背包的一个优化技巧:二进制优化。我们把一个数(n)拆成(1,2,4,8···)(k)(拆剩下的数),就可以表示位于区间([1,n])之内的所有数。为什么呢?因为前面的二进制数之和必定大于等于(k),如果(k)大于它们的话就可以拆出来一个更大的二进制数。那么由于前面的二进制数可以表示出([1,n-k])内的数(可以以二进制的角度来理解),所以加上(k)就能表示出([1,n])之内的数了。
      所以说我们只要先对之前的物品做一下预处理。对于单个物品,如果其总值不小于拥有的钱数的话,直接当成一个物品用完全背包就行了。如果其总值小于拥有的钱数,就把它用二进制优化拆成若干个物品来跑(01)背包。最后统计出所有的物品能够组成的不同面值的数量就行了。

    const int maxn = 1e5+10;
    int coin[105], c[105];
    bool dp[maxn];
    int main(void) {
        int n, m;
        while(~scanf("%d%d", &n, &m) && (n||m)) {
            zero(dp); dp[0] = 1;
            for (int i = 1; i<=n; ++i) scanf("%d", &coin[i]);
            for (int i = 1; i<=n; ++i) {
                scanf("%d", &c[i]);
                if (coin[i]*c[i]>=m)
                    for (int k = coin[i]; k<=m; ++k) //完全背包
                        dp[k] |= dp[k-coin[i]];
                else {
                    for (int j = 1; c[i]>=j; j<<=1) { //对多重背包进行二进制优化
                        for (int k = m; k>=coin[i]*j; --k)
                            dp[k] |= dp[k-coin[i]*j];
                        c[i]-=j;
                    }
                    if (c[i]) 
                        for (int k = m; k>=coin[i]*c[i]; --k)
                            dp[k] |= dp[k-coin[i]*c[i]];
                }
            }
            int sum = 0;
            for (int i = 1; i<=m; ++i) sum += dp[i];
            printf("%d
    ", sum);
        }
        return 0;
    }
  • 相关阅读:
    表格维护:弹出
    表格联动
    表单查询
    浅谈分治 —— 洛谷P1228 地毯填补问题 题解
    The Captain 题解
    网课集训记
    2020-1-20寒假集训记
    博客使用声明
    JZOJ P5829 string 线段树
    线段树--CF438D The Child and Sequence
  • 原文地址:https://www.cnblogs.com/shuitiangong/p/12660977.html
Copyright © 2011-2022 走看看