Coins POJ - 1742 (背包变形)

Coins POJ - 1742 (背包变形)

题目链接
题目大意:给n个硬币,问凑1~m中的数,能凑出几个?
题目样例:
3(n个硬币) 10(m)
1 2 4 2 1 1 前n个是硬币的面额,后n个是每个硬币面额的个数
2 5
1 4 2 1
输出:8 4

第一反应是多重背包,然后就去学了下。附代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn = 1000;
int value[maxn], amount[maxn], dp[100005];
int n, V; //n是个数, V是容量
//value价值 amount 数量 
//应该是物品所占的体积 价值 数量  此题物品所占体积和价值相等

void Completepark(int vol, int val) { //完全背包
    for(int i = vol; i <= V; i++)
        dp[i] = max(dp[i], dp[i - vol] + vol);
}

void ZeroOnepark(int vol, int val) { //01背包
    for(int i = V; i >= vol; i--)
        dp[i] = max(dp[i], dp[i - vol] + vol);
}

void Multiplepark(int vol, int val, int amount) { //多重背包
     if(amount * vol >= V) { //数量和体积的乘积大于总积 可直接看成完全背包来处理 相当于有无限个
        Completepark(vol, val);
     }
     else {
        int k = 1;
        while(k < amount) {
            ZeroOnepark(k * vol, k * val); //01背包
            amount -= k;
            k <<= 1;
        }
        if(amount) {
            ZeroOnepark(amount * vol, amount * val);
        }
     }
}

int main()
{
    while(~scanf("%d %d", &n, &V)) {
        if(n == 0 && V == 0) break;
        memset(dp, 0, sizeof(dp));
        for(int i = 0; i < n; i++) {
           scanf("%d", &value[i]);
        }
       for(int i = 0; i < n; i++) {
           scanf("%d", &amount[i]);
        }

        for(int i = 0; i < n; i++) {
           Multiplepark(value[i], value[i], amount[i]);
        }
        int num = 0;
        for(int i = 1; i <= V; i++) {
            if(dp[i] == i)
                num++;
        }
        printf("%d\n", num);
    }

}

然后就TLE了。。。看了poj的讨论区,发现这个题卡这种做法,然后就知道了还有一种更快的多重背包的做法,单调队列优化的,遂去学,然而没学会。。
然后看了挑战上面的做法,很棒的做法.

 dp[i][j] 表示的是用前i种去凑j这些容量,i还剩多少种
                    num[i]  if(dp[i][j] >= 0)
 dp[i][j]           -1  (j < num[i] || dp[i + 1][j - num[i]] < 0)
                    dp[i + 1][j - num[i]] - 1 else

然后就MLE了,把它换成1维的就ok

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int maxn = 500 + 100;
int num[maxn], value[maxn], dp[100001];

int main()
{
    int n, m;
    int ans;
    while(~scanf("%d %d", &n, &m)) {
        if(n == 0 && m == 0) break;
        for(int i = 1; i <= n; i++)
            scanf("%d", &num[i]);
        for(int i = 1; i <= n; i++)
            scanf("%d", &value[i]);

        memset(dp, -1, sizeof(dp));
        dp[0] = 1;
        for(int i = 1; i <= n; i++) {
            for(int j = 0; j <= m; j++) {
                if(dp[j] >= 0) dp[j] = value[i];
                else {
                    if(j < num[i] || dp[j - num[i]] < 0) dp[j] = -1;
                    else dp[j] = dp[j - num[i]] - 1;
                }
//                printf("%d %d %d\n", i, j, dp[i][j]);
            }
        }

         ans = 0;
        for(int i = 1; i <= m; i++) {
            if(dp[i] >= 0) ans++;
        }
        printf("%d\n", ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值