清华大学2007年机试-最小邮票数-1209
题目描述:
有若干张邮票,要求从中选取最少的邮票张数凑成一个给定的总值。
如,有1分,3分,3分,3分,4分五张邮票,要求凑成10分,则使用3张邮票:3分、3分、4分即可。
输入
有多组数据,对于每组数据,首先是要求凑成的邮票总值M,M<100。然后是一个数N,N<20,表示有N张邮票。接下来是N个正整数,分别表示这N张邮票的面值,且以升序排列。
输出
对于每组数据,能够凑成总值M的最少邮票张数。若无解,输出0。
样例输入
10
5
1 3 3 3 4
样例输出
3
解题思路:
这道题有两种解题思路:一是遍历,可以用广度优先遍历完成;二是动态规划,状态转移方程为: total[j] = min{total[j], total[j - stamp[i]] + 1}。
BFS代码:
#include <cstdio>
#include <queue>
using namespace std;
const int maxm = 100;
const int maxn = 20;
int M, N;
int stamp[maxn];
struct node{
int index;
int cnt;
int sum;
node(){}
node(int a, int b, int c): index(a), cnt(b), sum(c) {}
};
int bfs(){
int sum = 0, t = 0;
queue<node> que;
que.push(node(N, 0, 0));
while(!que.empty()){
node tmp = que.front();
sum = tmp.sum;
que.pop();
for(int i = tmp.index - 1; i >= 0; i--){
t = sum + stamp[i];
if(t > M) continue;
else if (t < M) que.push(node(i, tmp.cnt+1, t));
else return tmp.cnt+1;
}
}
return 0;
}
int main(){
//freopen("C:\\Users\\Administrator\\Desktop\\test.txt", "r", stdin);
while(scanf("%d", &M) != EOF){
scanf("%d", &N);
for(int i = 0; i < N; i++){
scanf("%d", &stamp[i]);
}
printf("%d\n", bfs());
}
//fclose(stdin);
return 0;
}
DP代码:
#include <cstdio>
#define INF 10000
const int maxm = 100;
const int maxn = 20;
int M, N;
int stamp[maxn];
int total[maxm];
int min(int a, int b){
return a<b?a:b;
}
int DP(){
for(int i = 1; i <= M; i++){
total[i] = INF;
}
total[0] = 0;
for(int i = 0; i < N; i++){
for(int j = M; j >= stamp[i]; j--){
if(total[j-stamp[i]] < INF){
total[j] = min(total[j], total[j-stamp[i]]+1);
}
}
}
return total[M] != INF?total[M]:0;
}
int main(){
freopen("C:\\Users\\Administrator\\Desktop\\test.txt", "r", stdin);
while(scanf("%d", &M) != EOF){
scanf("%d", &N);
for(int i = 0; i < N; i++){
scanf("%d", &stamp[i]);
}
printf("%d\n", DP());
}
fclose(stdin);
return 0;
}