硬币方案 (背包DP)
题目描述:
给定 N N N 种硬币,其中第 i i i 种硬币的面值为 a i a_i ai,共有 b i b_i bi 个。从中选出若干个硬币,把面值相加,若结果为 s s s,则称“面值 s s s 能被拼成”。求 1 1 1 到 M M M 之间能被拼成的面值有多少个。
输入格式:
输入包含多组测试数据。
每组测试数据第一行包含两个整数
n
n
n 和
M
M
M。
第二行包含2N个整数,分别表示
a
1
⋯
a
n
a_1\cdots a_n
a1⋯an 和
b
1
⋯
b
n
b_1\cdots b_n
b1⋯bn。
当输入
N
=
0
,
M
=
0
N=0,M=0
N=0,M=0 时,表示输入终止,且该数据无需处理。
输出格式:
每组用例输出一个结果,每个结果占一行。
样例输入:
3 10
1 2 4 2 1 1
2 5
1 4 2 1
0 0
样例输出:
8
4
数据范围:
1 < = N < = 100 1 < = M < = 1 0 5 1 < = a i < = c i < = 1000 1<=N<=100~~~~~~1<=M<=10^5~~~~~1<=a_i<=c_i<=1000 1<=N<=100 1<=M<=105 1<=ai<=ci<=1000
解题思路:
判断 1 ⋯ M 1\cdots M 1⋯M 以内有多少个 S S S ,其实就是判定每个 S S S 是否可行,考虑用动态规划。
设
f
i
f_i
fi 表示这些硬币能否凑出面值为
i
i
i 的数,若能
f
i
=
t
r
u
e
f_i=true
fi=true;否则
f
i
=
f
a
l
s
e
f_i=false
fi=false。
由于每个硬币有固定数量,我们当然不能一个硬币无止境的凑。因此设
c
n
t
i
,
j
cnt_{i,j}
cnti,j 表示凑成面值为
i
i
i时,第
j
j
j 种硬币最少要多少个。
很显然,若第 k k k 种硬币能凑出 i i i,那么 f i − a k = t r u e f_{i-a_k}=true fi−ak=true,而 c n t i , j = c n t i − a k , j + 1 cnt_{i,j}=cnt_{i-a_k,j}+1 cnti,j=cnti−ak,j+1,由于每个物品可以被多次选,因此 f i f_i fi 正序枚举。
可以推出状态转移方程:
f
i
=
{
t
r
u
e
f
i
−
a
k
=
t
r
u
e
&
c
n
t
i
−
a
k
,
j
<
b
j
k
=
1
⋯
n
f
a
l
s
e
o
t
h
e
r
s
f_{i}=\begin{cases} true&f_{i-a_k=true~\&~cnt_{i-a_k,j}<b_j~~~~~k=1\cdots n}\\ \\ false&others \end{cases}
fi=⎩⎪⎨⎪⎧truefalsefi−ak=true & cnti−ak,j<bj k=1⋯nothers
c
n
t
=
{
c
n
t
i
−
a
k
,
j
+
1
f
i
−
a
k
=
t
r
u
e
&
c
n
t
i
−
a
k
,
j
<
b
j
k
=
1
⋯
n
0
o
h
e
r
s
cnt=\begin{cases} cnt_{i-a_k,j}+1&f_{i-a_k=true~\&~cnt_{i-a_k,j}<b_j~~~~k=1\cdots n}\\ \\ 0&ohers \end{cases}
cnt=⎩⎪⎨⎪⎧cnti−ak,j+10fi−ak=true & cnti−ak,j<bj k=1⋯nohers
CODE:
#include <iostream>
#include <cstring>
using namespace std;
int n,m,a[110],b[110];
bool f[100010]={false};
int cnt[100010]={0};
int main()
{
cin>>n>>m;
while(n!=0||m!=0)
{
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
memset(f,false,sizeof(f));
f[0]=true;
cnt[0]=0;
for(int i=1;i<=n;i++)
{
memset(cnt,0,sizeof(cnt)); //当场更新 cnt ,可以省掉第二维硬币的种类
for(int j=a[i];j<=m;j++)
{
if(f[j]==false&&f[j-a[i]]==true&&cnt[j-a[i]]<b[i])
{
f[j]=true;
cnt[j]=cnt[j-a[i]]+1;
}
}
}
int ans=0;
for(int i=1;i<=m;i++)
if(f[i])
ans++;
cout<<ans<<endl;
cin>>n>>m;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!