0.序
偶然被老师强迫刷到了这样一道题,正好亿年没写题解了,来此练练手+摸鱼。
1.概述
基本思路:DFS暴力枚举+DP验证。
先DFS出来一个序列,然后DP出所有面值用到的最少邮票数。
设(f[i])为凑出(i)面值用到的最少邮票张数。
方程是我们在幼儿园就接触过的(f[i]=min(f[i],f[i-a[j]]+1))。
(a)数组为临时面值数组。
求出f数组之后就枚举到第一个大于n的就行了。
设计一下DFS:
void DFS(int dep,int now,int tmp)
dep:当前枚举了多少数;
now:当前数;
tmp:上一个最大面值。
然后就没了。
2.细节
for(rg int i=now+1;i<=tmp+1;i++){
a[dep]=i;
DFS(dep+1,i,DP(dep));
}
下界的now+1不用说,关键是上界。
i肯定是尽量往大里枚举,
但如果枚举到tmp+2的话中间就会空出一个数,不符合题目要求。
所以此处为tmp+1。(不多不少刚刚好)
2.
题目中没有给出最大面值为多少,所以f数组看着开吧。
3.代码
省去了又臭又长的缺省源。
int n,k,a[50],f[100010],ans[50],maxs;
inline int DP(int x){
memset(f,0x3f,sizeof(f));
f[0]=0;
for(rg int j=1;j<=x;j++){
for(rg int i=a[j];i<=a[x]*n;i++){
f[i]=min(f[i],f[i-a[j]]+1);
}
}
for(rg int i=1;i<=a[x]*n;i++){
if(f[i+1]>n)return i;
}
return a[x]*n;
}
void DFS(int dep,int now,int tmp){
if(dep>k){
if(tmp>maxs){
maxs=tmp;
for(rg int i=1;i<=k;i++){
ans[i]=a[i];
}
}
return;
}
for(rg int i=now+1;i<=tmp+1;i++){
a[dep]=i;
DFS(dep+1,i,DP(dep));
}
}
int main(){
Read(n),Read(k);
DFS(1,0,0);
for(rg int i=1;i<=k;i++){
cout<<ans[i]<<" ";
}
cout<<endl;
cout<<"MAX="<<maxs<<endl;
return 0;
}