P1021 [NOIP1999 提高组] 邮票面值设计(dfs&dp)

好题!和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[ia[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[x1]+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;
}