首先,看到这题,大家肯定首先想到暴力+dfs吧!

可是这题暴力会超时;

好吧我们还是来认真思考下正解

思路应该是枚举出所有种类的邮票,最后判断一下,并记录最大值

暴搜,不行的话,可以剪枝?

1.使a数组保持单调递增,dfs中每次从a[k-1]+1开始搜索,以此来消除重复的搜索;(常规思路)

2.a[1]=1;//每次肯定都要1这个面值的,可以思考一下,那么直接从a[2]开始枚举;

思考一个问题,dfs的枚举的下界已经清晰,可是上界该是多少呢?

这里我们来举个栗子吧~

假设当前准备填第k个(已经填好了k-1)个

前k-1个中可以凑出1-t中的所有整数(需要用dp求出t)

因此我们可以把上界定为t+1(而不是t,因为超过t+1就凑不出t+1,答案就没法+1了)

也就是说

    for(int j=a[k-1]+1;j<=end+1;j++){
        a[k]=j;dfs(k+1);a[k]=0; 
    }

//这里的end就是由dp求出来前k-1凑出的答案;

诶?怎么突然有了个dp出来(因为我们不知道前面的答案)

于是想到一个dp[j]表示a数组凑成j面值个数的最小值;

于是很容易得到

dp[j]=min(dp[j],dp[j-a[i]]+1);(dp[j-a[i]]<n)//显然现在的情况是小于等于n的;

初始化+oo,dp[0]=0;

code来啦!

#include<bits/stdc++.h> 
using namespace std;
int n,m,maxx=0;
int dp[51000],ans[25],a[25];
int solve(int k){
    memset(dp,63,sizeof(dp));dp[0]=0;
    for(int i=1;i<=k;i++)//前k个数 
        for(int j=a[i];j<=a[k]*n;j++)//a数组保持其单调性,最多能组成到a[k]*n,此时全部都选最大的数 
            if(dp[j-a[i]]<n)//只能继承的<n 
                dp[j]=min(dp[j],dp[j-a[i]]+1);//求最小值 
    int x=0;
    while(dp[x+1]<=100)x++;//统计结果 
    return x;
}
void dfs(int k){
    if(k==m+1){//如果已经找到m个 
        int t=solve(k-1);
        if(t>maxx){
            maxx=t;
            for(int i=1;i<=m;i++) ans[i]=a[i];
        }
        return;
    }
    int end=solve(k-1);
    for(int j=a[k-1]+1;j<=end+1;j++){
        a[k]=j;
        dfs(k+1);
        a[k]=0; //很重要的暴力枚举 
    }
}
int main(){
    cin>>n>>m;
    a[1]=1; //不管怎样都会选1; 
    dfs(2);
    for(int i=1;i<=m;i++)printf("%d ",ans[i]);
    printf("\nMAX=%d\n",maxx);//愉快输出答案! 
    return 0;
}
//dp+dfs;