有多组数据,对于每组数据,首先是要求凑成的邮票总值M,M<100。然后是一个数N,N〈20,表示有N张邮票。接下来是N个正整数,分别表示这N张邮票的面值,且以升序排列。
对于每组数据,能够凑成总值M的最少邮票张数。若无解,输出0。
10 5 1 3 3 3 4
3
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<bitset>
#include<ctime>
#include<deque>
#include<stack>
#include<functional>
#include<sstream>
//#include<cctype>
//#pragma GCC optimize(2)
using namespace std;
#define maxn 200005
#define inf 0x7fffffff
//#define INF 1e18
#define rdint(x) scanf("%d",&x)
#define rdllt(x) scanf("%lld",&x)
#define rdult(x) scanf("%lu",&x)
#define rdlf(x) scanf("%lf",&x)
#define rdstr(x) scanf("%s",x)
typedef long long  ll;
typedef unsigned long long ull;
typedef unsigned int U;
#define ms(x) memset((x),0,sizeof(x))
const long long int mod = 1e9;
#define Mod 1000000000
#define sq(x) (x)*(x)
#define eps 1e-5
typedef pair<int, int> pii;
#define pi acos(-1.0)
//const int N = 1005;
#define REP(i,n) for(int i=0;i<(n);i++)
typedef pair<int, int> pii;
inline int rd() {
    int x = 0;
    char c = getchar();
    bool f = false;
    while (!isdigit(c)) {
        if (c == '-') f = true;
        c = getchar();
    }
    while (isdigit(c)) {
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }
    return f ? -x : x;
}
ll gcd(ll a, ll b) {
    return b == 0 ? a : gcd(b, a%b);
}
int sqr(int x) { return x * x; }
/*ll ans;
ll exgcd(ll a, ll b, ll &x, ll &y) {
    if (!b) {
        x = 1; y = 0; return a;
    }
    ans = exgcd(b, a%b, x, y);
    ll t = x; x = y; y = t - a / b * y;
    return ans;
}
*/
int m;
int n;
int a[maxn];
int minn = inf;
int main()
{
    //ios::sync_with_stdio(0);
    cin >> m >> n;
    for (int i = 1; i <= n; i++)rdint(a[i]);
    sort(a + 1, a + 1 + n);
    for (int i = 0; i < (1 << 20); i++) {
        int sum = 0;
        int ct = 0;
        for (int j = 1; j <= n; j++) {
            if (i&(1 << (j - 1))) {
                sum += a[j]; ct++;
            //    cout << a[j] << ' ';
            }
        }
        if (sum == m) {
            minn = min(minn, ct);
        }
    //    cout << endl;
    }
    cout << minn << endl;
    return 0;
} 没有采用动态规划,而是尝试了深度优先搜索,代码丑陋,贴出来丰富一下大家的思路
#include<iostream>
using namespace std;
// M为邮票总值,n为邮票张数,minNum为用到的最少的张数
// N为邮票面额 
int M, n, minNum;
int N[20];
// index为邮票的编号,Num为当前用到的邮票张数,Sum为用到的邮票总面额 
void DFS(int index, int Num, int Sum){
    if(index == n){
        if(Sum == M && Num < minNum){
            minNum = Num;
        }
        return;
    }
    DFS(index + 1, Num + 1, Sum + N[index + 1]);
    DFS(index + 1, Num, Sum);
}
int main(){
    // 给minNum一个大于等于20的初值 
    minNum = 20;
    while(cin >> M){
        cin >> n;
        for(int i = 0; i < n; i++){
            cin >> N[i];
        }
        // 初始index为-1 
        DFS(-1, 0, 0);
        if(minNum == 20){
            minNum = 0;
        }
        cout << minNum << endl;
    }
    return 0;
}
                                                                                    #include<stdio.h>
int dp[20][105],Max=99999999;
int main(){
	int v[50],N,i,j,M;
	while(scanf("%d",&M)!=EOF){
		for(scanf("%d",&N),i=0;i<N;i++) scanf("%d",&v[i]);
		for(i=0;i<N;i++) dp[i][0]=0;
		for(j=0;j<=M;j++)
			if(j==v[0]) dp[0][j]=1;
			else dp[0][j]=Max;
		for(i=1;i<N;i++)
			for(j=1;j<=M;j++){
				dp[i][j]=dp[i-1][j];
				if(j>=v[i]&&dp[i][j]>dp[i-1][j-v[i]]+1)
					dp[i][j]=dp[i-1][j-v[i]]+1;
			}
		printf("%d\n",dp[N-1][M]<Max?dp[N-1][M]:0);
	}
} #include <iostream>
#include <math.h>
using namespace std;
int main(){
    int m, n, v[50], dp[20][100], Max = 99999999;
    while(scanf("%d",&m)!=EOF&&m>0){
        scanf("%d", &n);
        if(n==0){
            printf("0\n");
            break;
        }
        for(int i=1;i<=n;i++)
            scanf("%d",&v[i]);
        for(int i=0;i<=m;i++)
            dp[0][i] = Max;
        for(int i=0;i<=n;i++)
            dp[i][0] = 0;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++){
                if(v[i]>j) dp[i][j] = dp[i-1][j];
                else
                    dp[i][j] = min(1+dp[i-1][j-v[i]], dp[i-1][j]);
            }
        if(dp[n][m]==Max) printf("0\n");
        else printf("%d\n",dp[n][m]);
    }
}
 dfs,bfs都可以,速度都差不多
package com.speical.first;
import java.util.Scanner;
/** 
* 邮票问题
* 
* dfs
* 因为一张有票可以使用一次,所以dfs其实也挺快的
* 不用变量来维持就小
* 根据dfs特性,最后一个成功的,必然是数量最小的那个路径
* @author special
* @date 2017年12月21日 下午3:54:17
*/
public class Pro13 {
    private static int sum;
    private static int[] nums;
    private static int minCount;
    public static void dfs(int index, int add, int count){
        if(add == sum) {
            minCount = count;
            return;
        }
        for(int i = index + 1; i < nums.length; i++)
            dfs(i, add + nums[i], count + 1);
    }
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Scanner input = new Scanner(System.in);
        while(input.hasNext()){
            sum = input.nextInt();
            int n = input.nextInt();
            nums = new int[n];
            for(int i = 0; i < n; i++)
                nums[i] = input.nextInt();
            minCount = 0;
            dfs(-1,0,0);
            System.out.println(minCount);
        }
    }
}
#include<iostream>
using namespace std;
const int N = 20;
int k;
int value[N];
void DFS(int count,int n,int deep,int index)   
{
	if (n == 0)    k = min(deep, k);
	else
	{
		for (int i = index; i < count; i++)      //每个元素只会访问一次,减少访问序列
		{		
			if (n - value[i] < 0)     //数据是升序,大于某数其他的数的序列不访问
				break;
				DFS(count, n - value[i], deep + 1,i+1);
		
		}
	}
}
int main()                                      
{
	int n, count;
	while (cin >> n >> count)
	{
		k = count + 1;
		for (int i = 0; i < count; i++)
			cin >> value[i];
		DFS(count, n, 0,0);
		if(k!=count+1)
		cout << k << endl;
		else cout << 0 << endl;
	}
} #include<iostream>
#include<algorithm>
using namespace std;
int main()
{
	int M, N;
	while (cin>>M>>N)
	{
		int* P = new int[N];//面值
		for (int i = 0; i < N; i++)
			cin >> P[i];
		//dp数组表示将第i个邮票加入集合后 凑总量为j的面额 所需要的最少邮票数量
		int* dp = new int[M + 1];
		//base case
		for (int j = 0; j <= M; j++)
			dp[j] = M;
		dp[0] = 0;
		//状态转移
		for (int i = 0; i < N; i++)
			for (int j = M; j >= P[i]; j--)
				dp[j] = min(dp[j], dp[j - P[i]] + 1);
		if (dp[M] == M)//说明所有邮票加起来也凑不出来
			dp[M] = 0;
		cout << dp[M] << endl;
		delete[] P, dp;
	}
	return 0;
} dp[i][j] = min(dp[i-1][j], dp[i-1][j-value[i]]+1);在最后输出时,如果无解的话,dp肯定要大于upper的。所以对于 dp[n][m]>=upper,输出0表示无解
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <climits>
#include <cstring>
using namespace std;
int value[21];
int dp[21][101];  //dp[i][j]表示总值为j时,给定i张邮票所需要的最少邮票
// dp[i][j] = min(dp[i-1][j], dp[i-1][j-value[i]]+1)
bool cmp(int a, int b){
    return a>b;
}
int main(){
    int m, n;
    while(cin>>m>>n && m && n){
        for(int i=1; i<=n; i++){
            cin>>value[i];
        }
        int upper = n +100;//随便挑的一个最大值,只要比n大就行。保证正确答案永远取不到这个值
        for(int i= 0; i<=n; i++){
            for(int j=0; j<=m; j++){
                //下面这一对if else的顺序不能调换!
                if (j==0){//如果目标总值为0,不需要拿邮票,就是最小值0
                    dp[i][j]=0;
                    continue;
                }
                else if(i==0){//如果没有给邮票,那要达到目标总值肯定无解,定为需要无限张邮票
                    dp[i][j]=upper;
                    continue;
                }
                if(j<value[i]){//装不下
                    dp[i][j] = dp[i-1][j];
                }
                else{
                    //要么和前面一样,要么拿当前这张
                    dp[i][j] = min(dp[i-1][j], dp[i-1][j-value[i]]+1);
                }
            }
        }
//        for(int i=0; i<=n; i++){
//            for(int j=0; j<=m; j++){
//                cout<<dp[i][j]<<" ";
//            }
//            cout<<endl;
//        }
        if(dp[n][m]>=upper)//正确答案取不到的值,即无解
            cout<<0<<endl;
        else
            cout<<dp[n][m]<<endl;
    }
    return 0;
} dp[j] = min(dp[j], dp[j-v[i]]+1);注意:j一定是从后向前遍历,因为0-1背包只能取1次。从后遍历的话可以保证dp[j]在j之前的状态都是不含本件物品的。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstring>
 
using namespace std;
const int MAXN = 21;
const int MAXM = 101;
const int INF = 21;
int v[MAXN];
int dp[MAXM];
int n,m;
int main(){
    while(cin>>m){
        cin>>n;
        for(int i=1; i<=n; i++){
            cin>>v[i];
        }
        fill(dp, dp+MAXM, INF);
        dp[0] = 0;
        for(int i=1; i<=n; i++){
            for(int j=m; j>=v[i]; j--){
                dp[j] = min(dp[j], dp[j-v[i]]+1);
            }
        }
        if(dp[m]<INF)
            cout<<dp[m]<<endl;
        else cout<<0<<endl;
//        for(int i=0; i<=m; i++){
//            cout<<dp[i]<<" ";
//        }
//        cout<<endl;
    }
    return 0;
} // 由于样本数据集较小,采用dsp的方法写的
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 20;
int value[maxn] = {0};          // 存储邮票的面值
int main()
{
    
    int m;
    int n;
    while(cin >> m)
    {
        cin >> n;
        for(int i = 0; i < n; ++i)
        {
            cin >> value[i];
        }
        // 0-2^n-1
        int end = (1<<n) - 1;
        int count = -1;
        int sum = 0;
        int tmp = 0;
        for(int i = 0; i <= end; ++i)
        {
            sum = 0;
            tmp = 0;
            // cout << i << ":";
            for(int j = 0; j < n; ++j)
            {
                // cout << (i & (1 << j)) << " ";
                if(i & (1 << j))
                {
                    sum += value[j];
                    tmp ++;
                }
            }
            // cout << endl;
            if(sum == m)
            {
                if(count == -1)
                {
                    count = tmp;
                }
                else
                {
                    count = count < tmp ? count : tmp;
                }
            }
        }
        if(count == -1)
        {
            cout << 0 << endl;
        }
        else
        {
            cout << count << endl;
        }
    }
    return 0;
}
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN =101;
const int INF = 9999;
int stamp[MAXN];
int dp[MAXN];
int main()
{
    int m,n;
    while(scanf("%d%d",&m,&n) != EOF)
    {
        for(int i=0; i<n; ++i)
        {
            scanf("%d",&stamp[i]);
        }
        //sort(stamp,stamp+n);
        for(int i=0; i<=m; ++i)
        {
            if(i == 0)
            {
                dp[i] = 0;
            }
            else
            {
                dp[i] = INF;
            }
        }
        for(int i=0; i<n; ++i)
        {
            for(int j=m; j>=stamp[i]; --j)
            {
                dp[j] = min(dp[j],dp[j-stamp[i]]+1);
            }
        }
        if(dp[m] != INF)
        {
            printf("%d\n",dp[m]);
        }
        else
        {
            printf("0\n");
        }
    }
    return 0;
} 首先,这是一个背包问题。描述转化为“背包”,有N个物品,每个物品的质量为Vi,背包重量为M,求最少取多少个物品才能刚好装满背包,如果无法满足条件,返回0。
我们用INT32_MAX表示无法凑齐,设dp[i][j]为邮票为前i张时,刚好凑成j所需要的最小邮票张数,有以下状态公式:
状态公式的解释如下:
//
// Created by jt on 2020/8/26.
//
#include <iostream>
#include <vector>
 
using namespace std;
 
int main() {
    int M, N;
    cin >> M >> N;
    vector<int> v;
    for (int i = 0; i < N; ++i) { int a; cin >> a; v.push_back(a); }
    vector<vector<int> > dp(N + 1, vector<int>(M + 1, INT32_MAX));
    for (int i = 0; i <= N; ++i) { dp[i][0] = 0; }
    for (int i = 1; i <= N; ++i) {
        for (int j = 1; j <= M; ++j) {
            // 如果可以选择当前邮票
            if (j >= v[i-1] && dp[i-1][j-v[i-1]] != INT32_MAX) {
                dp[i][j] = min(dp[i-1][j-v[i-1]] + 1, dp[i-1][j]);
            } else {
                dp[i][j] = dp[i-1][j];
            }
        }
    }
    if(dp[N][M] == INT32_MAX) cout << 0 << endl;
    else cout << dp[N][M] << endl;
} #include<vector>
(721)#include<iostream>
using namespace std;
int main() {
    int m, n;
    while(cin>>m>>n) {
        vector<int> stamps(n, 0);
        for(int i=0; i<n; ++i) cin>>stamps[i];
        vector<vector<int>> dp(n+1, vector<int>(m+1));
        for(int i=0; i<=m; ++i) dp[0][i]=10000;
        for(int i=0; i<=n; ++i) dp[i][0]=0;
        
        for(int i=1; i<=n; ++i) {
            for(int j=1; j<=m; ++j) {
                if(stamps[i-1] <= j)
                    dp[i][j]=min(dp[i-1][j], dp[i-1][j-stamps[i-1]]+1);
                else
                    dp[i][j]=dp[i-1][j];
            }
        }
        if(dp[n][m]==10000) cout<<0<<endl;
        else cout<<dp[n][m]<<endl;
    }
} #include <stdio.h>
int a[20];
int count(int m,int top)
{
    int min=20;
    for(int i=top; i>=0; i--)
    {
        if(a[i]==m)
        {
            return 1;
        }
        if(a[i]<m)
        {
            int temp=count(m-a[i],i-1);
            if(temp && temp<min)
            {
                min=temp;
            }
        }
    }
    if(min-20)
    {
        return min+1;
    }
    return 0;
}
int main()
{
    int m,n;
    while(scanf("%d%d",&m,&n)!=EOF)
    {
        for(int i=0; i<n; i++)
        {
            scanf("%d",&a[i]);
        }
        printf("%d\n",count(m,n-1));
    }
    return 0;
} #include<stdio.h>
#include<stdlib.h>
#include<string.h>
#pragma warning(disable:4996)
int main()
{
	int sum;
	int num;
	int* value;
	int score[101];//(序号是邮票总分值),(值是邮票个数)
	while (scanf("%d", &sum) != EOF)
	{
		for (int i = 0; i < 101; ++i)
			score[i] = -1;
		//score[0]设为0
		score[0] = 0;
		scanf("%d", &num);
		value = (int*)malloc(sizeof(int)*num);
		for (int i = 0; i < num; ++i)
			scanf("%d", value + i);
		//前面都是准备活动
		//正菜,从第一张邮票遍历到最后一张
		for (int i = 0; i < num; ++i)
		{
			for (int j = 100; j >=0; --j)//从后往前遍历score
			{
				if (score[j] == -1)
					continue;
				if (j + value[i] > 100)
				{
					continue;
				}
				if (score[j + value[i]] == -1)//能组成的新的总分值
					score[j + value[i]] = score[j] + 1;
				else if (score[j + value[i]] > score[j] + 1)//有更小的邮票个数
					score[j + value[i]] = score[j] + 1;
			}
			
		}
		if (score[sum] == -1)
			printf("0\n");
		else
		{
			printf("%d\n", score[sum]);
		}
		free(value);
	}
} //从后往前遍历,因为不可以有重复的。 
#include <bits/stdc++.h>
using namespace std;
int main(){
	int m,n,w[110],dp[1010]={0};
	cin>>m>>n;
	for(int i=1;i<=n;i++){
		cin>>w[i];
	}
		for(int i=1;i<=m;i++){
			dp[i]=1<<30;
		} 
	for(int i=1;i<=n;i++){
		for(int j=m;j>=w[i];j--){
			dp[j]=min(dp[j],dp[j-w[i]]+1);
			
		}
	}
	if(dp[m]<(1<<30))cout<<dp[m];
	else cout<<0;
	return 0;
}
 //二维dp数组解法
#include<iostream>
#include<vector>
using namespace std;
int main() {
	int V, N;
	while (cin >> V >> N) {
		vector<int> value(N + 1);
		vector<vector<int>> dp(N + 1, vector<int>(V + 1));					//初始化dp数组
		for (int i = 1; i <= N; i++)
			cin >> value[i];												//输入每张邮票价值
		for (int i = 1; i <= N; i++) {
			for (int j = 1; j <= V; j++) {
				if (j == value[i])											//当前价值刚好等于第i张邮票价值
					dp[i][j] = 1;
				else if (j > value[i]) {									//当前价值大于第i张邮票价值
					if (dp[i - 1][j] && dp[i - 1][j - value[i]])
						dp[i][j] = (dp[i - 1][j] < dp[i - 1][j - value[i]] + 1 ? dp[i - 1][j] : dp[i - 1][j - value[i]] + 1);
					else if (dp[i - 1][j] && !dp[i - 1][j - value[i]])
						dp[i][j] = dp[i - 1][j];
					else if(!dp[i - 1][j] && dp[i - 1][j - value[i]])
						dp[i][j] = dp[i - 1][j - value[i]] + 1;
				}
			}
		}
		cout << dp[N][V] << endl;
	}
	return 0;
} //一维dp数组解法
#include<iostream>
#include<vector>
using namespace std;
int main(){
    int V, N, i, j;
    while(cin >> V >> N){
        vector<int> vec(N + 1);
        vector<int> dp(V + 1, -1);
        dp[0] = 0;
        for(i = 1; i <= N; i++)
            cin >> vec[i];                        //输入数据
        for(i = 1; i <= N; i++)                   //遍历每一张邮票
            for(j = V; j >= 1; j--)               //从大面值向小面值遍历是为了保证钱数“正好”
                if(j >= vec[i] && dp[j - vec[i]] != -1)
                    if(dp[j] == -1)
                        dp[j] = dp[j - vec[i]] + 1;
                    else
                        dp[j] = (dp[j] > dp[j - vec[i]] + 1 ? dp[j - vec[i]] + 1 : dp[j]);
        if(dp[V] == -1)
            cout << 0 << endl;
        else
            cout << dp[V] << endl;
    }
    return 0;
}
 /*
    动态规划,01背包问题,这次求的是最小值
    
    状态表示 f[i][j] 只从前i张邮票里面选,总价值>=j,花的最小邮票的数量
      
    状态计算         1.如果j=0,那么无解,如果i=0,那么也无解,结果为0         2.将集合分为包含第i张的和不包含第i张的
            如果不包含第i张,那么f[i][j]=f[i-1][j]
            如果包含第i张,那么f[i][j] = min(f[i][j],f[i-1][j-v[i]]+w[i]);             所以状态计算就是f[i][j] = min(f[i-1][j],f[i-1][j-v[i]]+1)
    然后再用滚动数组将其优化一下变成一维的就好
*/
#include <bits/stdc++.h>
using namespace std;
const int maxn = 110;
const int inf = 1<<30; // 非法状态
int v[maxn];
int f[maxn];
int m,n;
int main(){
	cin>>m>>n;
	for(int i=1;i<=n;i++) cin>>v[i]; // input
	for(int i=1;i<=m;i++) f[i] = inf; // 因为要求的是恰好凑到,那么一开始除了0之外其他都没有合法的解
	for(int i=1;i<=n;i++)
		for(int j=m;j>=v[i];j--)
			f[j] = min(f[j],f[j-v[i]]+1);
	if(f[m]<inf)
		cout<<f[m]<<endl;
	else
		cout<<0<<endl;
} //回溯法(dfs) 注意剪枝
#include <iostream>
(720)#include <algorithm>
#include <vector>
using namespace std;
(3420)#define INF 0x7F7F7F
int m;		//邮票面值
int n;		//张数
vector <int> v;
vector <int> x;
int min_n = INF;
int select_cnt()
{
	int cnt = 0;
	for (auto i : x)
		if (i == 1)
			cnt++;
	return cnt;
}
void dfs(int i, int total, int remain)
{
	if (i == n)
	{
		if (total == m && select_cnt() < min_n)
			min_n = select_cnt();
	}
	else
	{
		if (total + remain - v[i] >= m)				//不选择
		{
			x[i] = 0;
			dfs(i + 1, total, remain - v[i]);
		}
		if (total + v[i] <= m)					//选择
		{
			x[i] = 1;
			dfs(i + 1, total + v[i], remain - v[i]);
		}
	}	
}
int main()
{
	cin >> m >> n;
	for (int i = 0; i < n; i++)
	{
		int num; cin >> num;
		v.push_back(num);
		x.push_back(0);
	}
	int remain = 0;
	for (auto i : v)
		remain += i;
	dfs(0, 0, remain);
	min_n = min_n == INF ? 0 : min_n;
	cout << min_n;
} //动态规划,注意从后往前遍历
#include <iostream>
(720)#include <algorithm>
#include <vector>
using namespace std;
(3420)#define INF 0x7F7F7F
int m;		//邮票面值
int n;		//张数
vector <int> v;
int func()
{
	vector<int>dp(m + 1);
	for (int i = 0; i <= m; i++)
		dp[i] = i == 0 ? 0 : INF;
	for (int i = 0; i < n; i++)
	{
		for (int j = m; j >= 0; j--)
		{
			if (j >= v[i])
				dp[j] = min(dp[j], dp[j - v[i]] + 1);
		}
	}
	return dp[m];
}
int main()
{
	cin >> m >> n;
	for (int i = 0; i < n; i++)
	{
		int num; cin >> num;
		v.push_back(num);
	}
	int res = func();
	res = res == INF ? 0 : res;
	cout << res << endl;
} #include <bits/stdc++.h>
using namespace std;
int main() {
    int m, n;  // 集齐m,  n张邮票
    const int INF = 1000;
    while (cin >> m >> n) {
        int stamp[n + 1];
        int dp[n + 1][m + 1]; // 前n个邮票,集齐m, 所需要的邮票数
        /*
         dp[i][j] 前i个邮票,集齐j, 所需要的邮票数
         -若第i个邮票不放入,则相当于前i-1张邮票要集齐j, dp[i][j] = dp[i - 1][j]
         -若第j个邮票放入,则相当于前i-1张邮票,集齐j-stamp[i],然后票数加一, dp[i][j] = dp[i - 1][j - stamp[i]] + 1;
         两者取最小值, dp[i][j] = min{dp[i - 1][j], dp[i - 1][j - stamp[i]] + 1 | j > stamp[i]}
         dp[n][m]即为所求。
         那么dp数组如何初始化?边界值设置为几?
         根据定义 -当dp[0...n][0]时,不管有多少张邮票,都要凑成0,则显而易见,一张邮票都不用就行,所以dp[0...n][0] = 0
                 - 当dp[0][1...m]时,没有一张邮票给你,但是要凑 大于0的数,则不可能,所以dp[0][1...m] = INF  (一个很大的数,别是INT_MAX,会爆int)
         做完以上工作,下来就是套01背包的板子
         */
        for (int i = 0; i <= n; ++i) {
            for (int j = 0; j <= m; ++j) {
                if (j == 0) dp[i][j] = 0;
                else dp[i][j] = INF;
            }
        }
        for (int i = 1; i <= n; ++i) {
            scanf("%d", &stamp[i]);
        }
        for (int i = 1; i <= n; ++i) {
            for (int j = m; j >= stamp[i]; --j) {
                    dp[i][j] = min(dp[i - 1][j], dp[i - 1][j - stamp[i]] + 1);
            }
        }
        int res = (dp[n][m] == INF) ? 0 : dp[n][m];
        cout << res << endl;
    }
    return 0;
} #include <iostream>
#include <iomanip>
#include <algorithm>
#include <functional>
#include <cmath>
#include <limits>
using namespace std;
const int INT_INF = numeric_limits<int>::max();
int m;
int ans = INT_INF;
int post[20];
int n;
void func(int sum,int num,int i){
	if(sum==m){  //凑成功,不断取最小值
		ans = min(ans,num);
		return ;
	}
	if(sum <m && i<n && num<ans){  //不满足时可减去枝丫
		func(sum+post[i],num+1,i+1);
		func(sum,num,i+1);
	}
}
int main(){
	while(cin>>m && m!=0){
		cin>>n;
		for(int i=0;i<n;i++)
			cin>>post[i];
		//输入完毕
		sort(post,post+n,greater<int>());  //排序,大的在前,方便减枝
		func(0,0,0);
		if(ans==INT_INF) cout<<0<<endl;
		else cout<<ans<<endl;
	}
	return 0;
}
 遍历所有情况,配合减去枝丫,其实情况不会很多。0<=i<=n-1,是否取第i个