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;
    }
  • 相关阅读:
    HTML
    JavaScript事件对象
    JavaScript 事件入门
    如何向github上传文件
    crystal
    BUUCTF[强网杯 2019]随便注wp
    BUUCTF[CISCN2019 华东南赛区]Web4 wp
    BUUCTF[NPUCTF2020]ezinclude wp
    Windows下Ant的环境配置
    Java解惑(1)——表达式之谜
  • 原文地址:https://www.cnblogs.com/shuitiangong/p/12660977.html
Copyright © 2011-2022 走看看