【AcWing 281】 硬币 多重背包问题

这是一道关于背包问题的动态规划(DP)题目,要求求解在给定的N种硬币面值和数量限制下,能拼成1到M之间面值的总数。输入包括硬币种类数N,最大面值M,以及每种硬币的面值Ai和数量Ci。由于Ci值较大,不适合用01背包的方法。解题思路是建立dp数组,通过遍历硬币面值和数量,更新dp状态,最后累加计数得出结果。

给定N种硬币,其中第 i 种硬币的面值为Ai,共有Ci个。

从中选出若干个硬币,把面值相加,若结果为S,则称“面值S能被拼成”。

求1~M之间能被拼成的面值有多少个。

输入格式
输入包含多组测试用例。

每组测试用例第一行包含两个整数N和M。

第二行包含2N个整数,分别表示A1,A2,…,AN和C1,C2,…,CN。

当输入用例N=0,M=0时,表示输入终止,且该用例无需处理。

输出格式
每组用例输出一个结果,每个结果占一行。

数据范围
1≤N≤100,
1≤M≤105,
1≤Ai≤105,
1≤Ci≤1000
输入用例:
3 10
1 2 4 2 1 1
2 5
1 4 2 1
0 0
输出用例:
8
4

题意:如题

思路:

多重背包,看到数据范围肯定最多也就O(nm)的时间复杂度了。而且Ci的值很大,不能分散成一个个来当01背包。
用dp[j]表示能否凑到i这么大的面值。
我们用每个a[i].val(面值)去填 dp[j],那dp[j]能否被填好取决于dp[j-a[i].val]有没有被填好,同时在填那个位置的时候剩下来的a[i]要大于等于1 。
然后处理完之后遍历一遍1->m,累加计数即可。

AC代码:

#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include <queue>
#include<sstream>
#include <stack>
#include <set>
#include <bitset>
#include<vector>
#define FAST ios::sync_with_stdio(false)
#define abs(a) ((a)>=0?(a):-(a))
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define rep(i,a,n) for(int i=a;i<=n;++i)
#define per(i,n,a) for(int i=n;i>=a;--i)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PII;
const int maxn 
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值