有若干张邮票,要求从中选取最少的邮票张数凑成一个给定的总值。
如,有 1 分,3 分,3 分,3 分,4 分五张邮票,要求凑成 10 分,则使用 3 张邮票:3 分、3 分、4 分即可。
输入格式
第一行包含整数 M,表示需要凑成的邮票总值。
第二行包含整数 N,表示给定邮票数量。
第三行包含 N 个整数,表示每个邮票的面值,以升序排列。
输出格式
输出能够凑成总值 M 所需的最少邮票张数。
若无解,则输出 0。
数据范围
1≤M≤100,
1≤N≤20,
每张邮票的面值范围 [1,100]。
输入样例:
10
5
1 3 3 3 4
输出样例:
3
解法1:dfs
#include<iostream>
#include<cstring>
using namespace std;
int m,n;
const int N=10010;
int a[25];
bool st[25];
int cnt;
int ans=2e9;
void dfs(int x,int sum)
{
if(sum==m)
{
ans=min(ans,cnt);
}
else if(x>0)
{
st[x]=true;
sum+=a[x];//当前总和
cnt++;
//cout<<sum<<" "<<cnt<<endl;
dfs(x-1,sum);//选
st[x]=false;
sum-=a[x];
cnt--;
dfs(x-1,sum);//不选
}
return;
}
int main()
{
cin>>m>>n;
memset(a,0x3f, sizeof a);//初始化为无穷
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
dfs(n,0);
if(ans==2e9) ans=0;
cout<<ans<<endl;
}
解法2:01背包,从m张邮票选出价值为n的最小方案数
f[i][j] = min (f[ i - 1 ] [ j ], f[ i - 1 ] [ j - a[i] ] + 1);
二维
#include<iostream>
#include<cstring>
using namespace std;
int m,n;
const int N=110;
int a[25];
int f[25][N];
int main()
{
cin>>m>>n;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i = 1;i <= m;i++) f[0][i] = 0x3f3f3f3f;
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
{
f[i][j] = f[i-1][j];
if(j>=a[i]) f[i][j]=min(f[i-1][j], f[i-1][j-a[i]]+1);
}
if(f[n][m]==0x3f3f3f3f) cout<<0<<endl;
else cout<<f[n][m]<<endl;
}
一维
#include<iostream>
#include<cstring>
using namespace std;
int m,n;
const int N=110;
int a[25];
int f[N];
int main()
{
cin>>m>>n;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i = 1;i <= m;i++) f[i] = 0x3f3f3f3f;
for(int i=1;i<=n;i++)
for(int j=m;j>=a[i];j--)
{
f[j]=min(f[j], f[j-a[i]]+1);
}
if(f[m]==0x3f3f3f3f) cout<<0<<endl;
else cout<<f[m]<<endl;
}