BUPT Summer Journey #test4 E

 

时间限制 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;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值