好题!和ICPC昆明的一道主席树思想相同。
思路:先利用dp找到 f [ i ] f[i] f[i]表示到达面值 i i i所需的最小邮票数。
具体地,转移方程是: f [ i ] = m i n ( f [ i ] , f [ i − a [ i ] ] + 1 ) f[i]=min(f[i],f[i-a[i]]+1) f[i]=min(f[i],f[i−a[i]]+1)。
当 f [ i ] > n f[i]>n f[i]>n就 b r e a k break break掉, i − − i-- i−−。
此时的能表示的最大面值 m x = i mx=i mx=i。
考虑一个问题,显然对于第 x x x张邮票的面值,显然最大只能是 m x + 1 mx+1 mx+1,如果大于 m x + 1 mx+1 mx+1,则 m x + 1 mx+1 mx+1表示不了。
所以可以暴力枚举 a [ x ] ∈ [ a [ x − 1 ] + 1 , m x + 1 ] a[x]\in[a[x-1]+1,mx+1] a[x]∈[a[x−1]+1,mx+1]。
然后 d f s ( x + 1 ) dfs(x+1) dfs(x+1)。
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=16,M=505,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define IOS ios::sync_with_stdio(false),cin.tie(0)
void Print(int *a,int n){
for(int i=1;i<n;i++)
printf("%d ",a[i]);
printf("%d\n",a[n]);
}
int n,k;
int a[N],b[N];
int f[M];
int ans;
void dfs(int x){
int mx=1;
for(int i=0;i<M;i++) f[i]=inf;
f[0]=0;
for(;;mx++){
for(int i=1;i<x&&a[i]<=mx;i++)
f[mx]=min(f[mx],f[mx-a[i]]+1);
if(f[mx]>n) break;
}
mx--;
if(x==k+1){
if(mx>ans){
ans=mx;for(int i=1;i<=k;i++) b[i]=a[i];
}
return;
}
for(int i=a[x-1]+1;i<=mx+1;i++){
a[x]=i;
dfs(x+1);
}
}
void solve(){
//think twice code once
scanf("%d%d",&n,&k);
a[1]=1;
dfs(2);
for(int i=1;i<=k;i++) printf("%d ",b[i]);
printf("\nMAX=%d\n",ans);
}
int main(){
solve();
return 0;
}