动态规划: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;
}
}