wiki 1255 搭积木

动态规划:dp[a][b][c]表示积木总个数为a,高度为b,当前层次中积木个数为c的时候的组合个数。因此我们最终组合方案为dp[n][h][m]。状态转移方程为dp[a][b][c] = dp[a-c][b-1][c-1]+dp[a-c][b-1][c+1]。最后求出第k大的方案时,我们不需要事先把所有的方案都遍历得到。比如说dp[n][h][m]个方案中,我们可以分成dp[n-m][h-1][m-1]个方案和dp[n-m][h-1][m+1]个方案的。因此在状态转移时我们选择了m-1,则对应第一个方案到第dp[n-m][h-1][m-1]个方案。而要是选择的m+1,则对应dp[n-m][h-1][m-1]+1到dp[n][h][m]个方案。这里可能表述的不清楚。。。看源代码吧。。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<iomanip>
#include<queue>
#include<cmath>
#include<stack>
#include<map>
#include<vector>
#include<set>
#include<algorithm>
using namespace std;

const int INF = 1 << 30;

long long n,m,h;
long long k,cur;
long long dp[550][70][11];
int vis[550][70][11];
void init(){
    cin >> n >> h >> m;
    memset(dp, 0, sizeof(dp));
    memset(vis, 0, sizeof(vis));
    for(int i = 1; i < 550; i++){
        for(int j = 1; j < 11; j++){
            if(i==j) {
                dp[i][1][j] = 1;
            }
            vis[i][1][j] = 1;
        }
    }
}
long long digui (int a, int b, int c){
    if(vis[a][b][c]) return dp[a][b][c];
    if(a <= c) {
        dp[a][b][c] = 0;
        vis[a][b][c] = 1;
        return 0;
    }
    dp[a][b][c] = 0;
    if(c>1){
        dp[a][b][c] += digui(a-c,b-1,c-1);
    }
    if(c<10){
        dp[a][b][c] += digui(a-c,b-1,c+1);
    }
    vis[a][b][c] = 1;
    return dp[a][b][c];
}

int main(int argc, const char * argv[])
{
    init();
    long long result = digui(n,h,m);
    cout << result << endl;

    cin >> k;
    
    while(k!=-1){
        vector<long long> myresult;
        long long s = 1;
        long long t = result;
        long long ta,tb,tc;
        ta = n;
        tb = h;
        tc= m;
        myresult.push_back(tc);
        for(int i = h-1; i >=1; i--){
            long long t1 = 0;
            if(tc > 1) t1 = dp[ta-tc][i][tc-1];
            long long t2 = 0;
            if(tc < 10) t2 = dp[ta-tc][i][tc+1];
            if(k>=s && k<=s+t1-1){
                ta = ta-tc;
                tc = tc - 1;
                myresult.push_back(tc);
                t = s+t1-1;
            }else{
                ta = ta-tc;
                tc = tc+1;
                myresult.push_back(tc);
                s = t-t2+1;
            }
        }
        for(int i = 0; i < h-1; i++) printf("%lld ", myresult[i]);
        printf("%lld\n",myresult[h-1]);
        cin >> k;
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值