最小邮票数
原创
©著作权归作者所有:来自51CTO博客作者不想悲伤到天明的原创作品,请联系作者获取转载授权,否则将追究法律责任
题目描述
有若干张邮票,要求从中选取最少的邮票张数凑成一个给定的总值。 如,有1分,3分,3分,3分,4分五张邮票,要求凑成10分,则使用3张邮票:3分、3分、4分即可。
输入描述:
有多组数据,对于每组数据,首先是要求凑成的邮票总值M,M<100。然后是一个数N,N〈20,表示有N张邮票。接下来是N个正整数,分别表示这N张邮票的面值,且以升序排列。
输出描述:
对于每组数据,能够凑成总值M的最少邮票张数。若无解,输出0。
示例1
输入
复制
10
5
1 3 3 3 4
输出
复制
3
思路 : 暴力的枚举结合二进制,n位的二进制表示邮票的选择,0表示不选择,1表示选择,然后从1,枚举到31,
判断和是否有等于 M的,统计一下用的邮票数,并记录最小邮票数。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std ;
const int MAX = 10005 ;
const int inf = 0x3f3f3f3f ;
int m, n ; // 总值 和 邮票数
int a[MAX] ;
int p[MAX] ;
const int mod = 1e9+7 ;
bool cmp(const int &x , const int &y) {
return x > y ;
}
int qpow(int a , int b) {
int ans = 1 ;
while(b){
if(b&1){
ans = ans*a %mod ;
}
a = a*a %mod ;
b>>=1 ;
}
return ans ;
}
int main(){
while(cin >> m >> n ) {
for(int i = 0 ; i<n ; i++) {
cin >>a[i] ;
}
// 0 - 31 00000 - 11111
int cnt = 0 ;
int minn = 20 ;
int sum = 0 ;
for(int i = 1 ; i<qpow(2,n) ; i++ ) {
int t = i ;
for(int j = 0; j<n ; j++ ) {
p[j] = t%2 ;
t = t/2 ;
if(p[j] == 1 ) cnt++ ; // 计算用的邮票数
}
for(int k = 0 ; k<n ; k++ ) {
if(p[k]){
sum+=a[k];
}
}
if(sum == m) { // 如果能凑够并且
if(cnt < minn) {
minn = cnt ; // 求出最小数
}
}
sum = 0 ;
cnt = 0 ;
}
if(minn == 20) {
minn = 0 ;
}
printf("%d\n",minn);
}
return 0 ;
}
思路 2 : 用深度优先搜索
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std ;
const int MAX = 10005 ;
const int inf = 0x3f3f3f3f ;
int m, n ; // 总值 和 邮票数
int a[MAX] ;
const int mod = 1e9+7 ;
int minn = 20 ;
void dfs(int index , int sum , int num) {
// sum 记录邮票的总值,num记录邮票数量
if(sum == m ) {
minn = num ;
return ;
}
for(int i = index + 1 ; i<n ; i++) {
dfs(i,sum+a[i] ,num+1) ;
}
}
int main(){
while(cin >> m >> n) {
for(int i = 0 ;i<n ; i++ ) {
scanf("%d",&a[i]) ;
}
minn = 20 ;
dfs(-1,0,0) ;
if(minn == 20 ) minn = 0 ;
printf("%d\n",minn);
}
return 0 ;
}