codevs 1255 搭积木 x

1255 搭积木

 时间限制: 1 s

 空间限制: 128000 KB

 题目等级 : 大师 Master

题目描述 Description

一种积木搭建方式,高为H的积木,最底层有M个积木,每一层的积木数是他的低一层的积木数+1-1。总共有N个积木。(且每行积木数不超过10

 

比如下图N=13 H=6 M=2

 

 

输入描述 Input Description

第一行为三个整数、NHM

第二行以后每行一个整数K-1为结束符。

输出描述 Output Description

第一行为满足NHM的积木搭建方案总数(1<=N<=540 H<=60 M<=10

以后每一行对于对应的K,给出顺序排列的第K种方案(最小的排列为第一种)。

(如样例中,2 1 2 3 2 3是一种方案,代表一层的积木分别为212323232321也是一种方案,212323232321要小,每个状态之间是可比的,第一个数小的排前面,第一个数相等的就看第二个数。那么所有方案就有一个顺序了,这里的K就是求第K个按顺序排列的方案)

样例输入 Sample Input

13 6 2

1

3

 -1

样例输出 Sample Output

3

2 1 2 3 2 3

2 3 2 3 2 1

思路:

  其实有两种思路,一种是DP,另一种就是记忆化搜索.

坑点:

  在记忆化搜索时,剪枝是必不可少的!最优剪枝应该是当 n/h>10 或者是 n/h<1 时,是绝对不可能的!搜索速度大大提高!

代码:

这里暂时先给出记忆化搜索的代码~

/*
作者:狐白酒(my name on the codevs!)
题目:p1255 搭积木
*/

#include <iostream>
#include <cstdio>
#define LL long long

using namespace std;

int N,H,M;
bool v[62][542][12];
LL f[62][542][12];

LL dfs(int h,int n,int m)///high,sum,down
{
    if(n/h>10 || n/h<1) return f[h][n][m]=0;
///这里剪枝剪的特多~
    if(v[h][n][m]) return f[h][n][m];
    else
    {
        if(h==1)
        {
            v[h][n][m]=1;
            if(n==m && m<=10)
                return f[h][n][m]=1;
            else 
                return f[h][n][m]=0;
        }
        if(h>=2)
        {
            LL s=0;
            if(m-1>=1)
                s+=dfs(h-1,n-m,m-1);
            if(m+1<=10)
                s+=dfs(h-1,n-m,m+1);
            v[h][n][m]=1;
            return f[h][n][m]=s;
        }
    }
}

void ask(LL x,int h,int n,int m)
{
    printf("%d ",m);
    if(h>=2)
    {
        if(x<=f[h-1][n-m][m-1])
            ask(x,h-1,n-m,m-1);
        else 
            ask(x-f[h-1][n-m][m-1],h-1,n-m,m+1);
    }
}

int main()
{
    scanf("%d%d%d",&N,&H,&M);
    dfs(H,N,M);
    cout<<f[H][N][M]<<endl;
    LL k;
    while(cin>>k)
    {
        if(k==-1) break;
        ask(k,H,N,M);
        printf("\n");
    }
    return 0;
}
View Code

过几天再把动规的补上~

 

 

End.

posted @ 2017-06-26 21:22  夜雨声不烦  阅读(226)  评论(0编辑  收藏  举报