E. 焦级长搭积木 2014新生暑假个人排位赛04
时间限制 1000 ms
内存限制 65536 KB
题目描述
焦级长特别喜欢搭积木,一天他创造了一种新的玩法。焦级长一共有N个积木,从下往上一共搭了H层,其中最底层有M个积木,除最底层,每一层的积木数是它下一层积木数+1或-1且每层积木不超过10个。
输入格式
input 输入含多组数据。每组第一行为三个整数N,H,M,第二行后每行一个整数K,以-1结束(1<=N<=540,H<=60,M<=10,K<=10^10)。
输出格式
output 第一行是满足N、H、M的积木搭建方案总数,以后每一行对于对应的K,给出顺序排列的第K种方案(最小的排列为第一种)。 如样例中,2 1 2 3 2 3是一种方案,代表一层的积木数从下往上分别为212323,232321也是一种方案,212323比232321要小,即第一个数小的排前面,第一个数相等的就看第二个数,以此类推。 这里的K就是求第K个按顺序排列的方案。
输入样例
13 6 2
1
3
-1
输出样例
3
2 1 2 3 2 3
2 3 2 3 2 1
思路:直接把题目中给出的三个变量当做状态,设F[N][H][M]为N块积木,H层需要用M块积木的总方法数。然后转移,可以由H-1层转移而来。即H-1层可以拼M+1,M-1块积木,则F[N][H][M]=F[N-M][H-1][M+1]+F[N-M][H-1][M-1]。如果你直接估计该搜索树的节点数,你会发现有2^H(1<=H<=60)个状态,会超。但其实M限定在1到10之间,最多10层后就变成了单链,故复杂度为O(2^10*50)就可以过了。打印方案第k种拼法,由于字典序最下,先考虑拼M-1块,若方法数小于K或该状态不合法则考虑M+1块,一遍搜索即可。由于方案数肯定有合法状态,故不会超时。
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
//#define LOCAL
usingnamespace std;
intN,H,M;
longlong K,f[600][100][20];
voidDP(intN,intH,intM)
{
if(f[N][H][M]!=-1)return;
if(H==1&&N==M&&M!=0){f[N][H][M]=1;return;}
elseif(H==1&&(N!=M||M==0)){f[N][H][M]=0;return;}
f[N][H][M]=0;
if(N-M>0&&H-1>=1)
{
if(M-1>=1)
{
DP(N-M,H-1,M-1);
if(f[N][H][M]==-1)f[N][H][M]=f[N-M][H-1][M-1];
elsef[N][H][M]+=f[N-M][H-1][M-1];
}
if(M+1<=10)
{
DP(N-M,H-1,M+1);
if(f[N][H][M]==-1)f[N][H][M]=f[N-M][H-1][M+1];
elsef[N][H][M]+=f[N-M][H-1][M+1];
}
return;
}
}
voidSearchWay(intN,intH,intM,intK)
{
//printf("N=%d H=%d M=%d\n",N,H,M);
if(H!=1)printf("%d ",M);
elseprintf("%d\n",M);
if(M-1>0&&H-1>=1&&N-M>0)
{
if(f[N-M][H-1][M-1]<K){if(M+1<=10&&H-1>=1&&N-M>0)SearchWay(N-M,H-1,M+1,K-f[N-M][H-1][M-1]);}
elseSearchWay(N-M,H-1,M-1,K);
}
elseif(M+1<=10&&H-1>=1&&N-M>0)SearchWay(N-M,H-1,M+1,K);
}
intmain()
{
#ifdef LOCAL
freopen("input.txt","r",stdin);
#endif // LOCAL
while(scanf("%d%d%d",&N,&H,&M)==3)
{
//if(N==-1)break;
memset(f,-1,sizeof(f));
DP(N,H,M);
printf("%lld\n",f[N][H][M]);
while(scanf("%lld",&K)==1)
{
if(K==-1)break;
SearchWay(N,H,M,K);
}
}
return0;
}