解题报告见黑书p257。
以下转自http://blog.csdn.net/sj13051180/article/details/6669737
POJ1037, a decorative fence。这个题绝对不是入门级的,如果能独立做出来,那已经很NB了。嘿嘿,这一大段英文,先要把题目看懂就得费些气力。
问题:有n个长度不同的木条,现在按照类似字典顺序(长度小的排在前)对这n个木条排序,求第m个图像是什么?
设这n个木条分别为S1 S2 S3…Sn,它们的对应长度分别为1 2 3...n.
对这n个木条进行排序,结果无非有下面n类: 以S1开头,以S2开头…以Sn开头。按照题目要求对这n类进行排序。
S1 。。。
S2 。。。
。。。
Sn 。。。
如果能算出每个区间有多少种排列,那么就能确定要求的第m个排列了。好抽象呀,还是举个例子吧。如果以S1开头的有10种排列,以S2开头的有10 种排列…现在有n个木条,如果要确定第15个排列,那么就可以知道第15个排列必然是以S2开头的。因为已经知道了最终排列的第一个木条是S2,那么只要 依次求出后面n-1个木条即可。如果能把S2开头的区间再划分成若干个区间,并且知道每个区间的大小就和上面的问题一样了。
S2 S1 。。。
S2 S3 。。。
。。。
S2 Sn 。。。
这时问题就转换为:有n-1个木条,现在要确定第5个排列(15-10=5,以S1开头的排列已占去了10个)
现在结合题目,题目要求出现波浪形,即有高度变换。这时以Si开头的排列又可以分成两类:第二个木条比Si高,我们记做Hi, 第二个木条比Si低,我们记做Li。这时区间细分成如下形式:
L1 。。。
H1 。。。
L2 。。。
H2 。。。
。。。。。。
Ln 。。。
Hn 。。。
要确定最终的排列,关键是要知道每个区间里排列的个数。
设L( x,n )表示n个木条的排列中,以长度为x的木条开始,且下一个木条比X低的排列数。H( x,n )表示n个木条的排列中,以长度为x的木条开始,且下一个比X高的的排列数
状态转换方程:
L( x, n ) = , 其中 1 <= i < x
H( y, n ) = , 其中 y <= i < n
有状态方程在手,就可以依次确定要求排列的每个木条。
参考资料:
http://jay23jack.blog.163.com/blog/static/317951942009130215813/
http://blog.163.com/leyni@126/blog/static/16223010220103150173663/
#include <iostream> using namespace std; //***********************常量定义***************************** const int MAX_N = 25; //*********************自定义数据结构************************* //********************题目描述中的变量************************ //木条的数目 int n; //栅栏的编号 long long c; //**********************算法中的变量************************** //up[n][i]表示:n个木条,且第一个木条长度为i的上升型栅栏数目 long long up[MAX_N][MAX_N]; //down[n][i]表示:n个木条,且第一个木条长度为i的下降型栅栏数目 long long down[MAX_N][MAX_N]; bool used[MAX_N]; bool isPrint; //***********************算法实现***************************** void FillTable() { down[1][1] = 1; up[1][1] = 1; down[2][1] = 0; up[2][1] = 1; down[2][2] = 1; up[2][2] = 0; int i, j, k; for( i=3; i<MAX_N; i++ ) { //??? j<=MAX_N for( j=1; j<MAX_N; j++ ) { //??? k=j for( k=j; k<i; k++ ) { up[i][j] += down[i-1][k]; } for( k=1; k<j; k++ ) { down[i][j] += up[i-1][k]; } } } } void SolveAndPrint( int id, int count ) { for( int i=1; i<=count; i++ ) { if( used[i] && i<=id ) { id++; count++; } } used[id] = true; if( isPrint ) { cout << " " << id; } else { cout << id; isPrint = true; } } //************************main函数**************************** int main() { //freopen( "in.txt", "r", stdin ); int caseNum; cin >> caseNum; //DP的填表 FillTable(); while( caseNum-- ) { cin >> n >> c; //先搜索down bool isDown = true; bool isFirst = true; int id = 1; isPrint = false; memset( used, 0, sizeof(used) ); while( n ) { if( isDown ) { if( c > down[n][id] ) { c -= down[n][id++]; if( isFirst ) { //设置标记,下次搜索up isDown = false; //对每个id有down和up两个值 //确定第一个数时,down和up都要搜索 id--; } } else { //如果当前木条长度可确定 //问题规模减一 SolveAndPrint( id, n-- ); isFirst = false; isDown = false; id = 1; } } else { if( c > up[n][id] ) { c -= up[n][id++]; if( isFirst ) isDown = true; } else { SolveAndPrint( id, n-- ); isFirst = false; isDown = true; } } } cout << endl; } return 0; }
以上转自http://blog.csdn.net/sj13051180/article/details/6669737
mycode:
/** * Problem:POJ1037 * Author:Shun Yao * Time:2013.5.18 * Result:Accepted * Memo:DP */ #include <cstring> #include <cstdlib> #include <cstdio> using namespace std; class Gnode { public: long long down, up; Gnode() { down = up = 0; } ~Gnode() {} } g[22][22]; char used[22], isprint; void SolveAndPrint(long id, long count) { static long i; for (i = 1; i <= id && i <= count; ++i) if (used[i]) { ++id; ++count; } used[id] = 1; if (isprint) putchar(' '); else isprint = 1; printf("%ld", id); } int main() { static long i, j, k, K, n, id; static long long c; static char isdown, isfirst; freopen("poj1037.in", "r", stdin); freopen("poj1037.out", "w", stdout); g[1][1].down = g[1][1].up = 1; for (i = 2; i <= 20; ++i) { for (j = 1; j <= i; ++j) { g[i][j].down = g[i][j].up = 0; for (k = 1; k < j; ++k) g[i][j].down += g[i - 1][k].up; for (k = j; k < i; ++k) g[i][j].up += g[i - 1][k].down; } } scanf("%ld", &K); while (K--) { scanf("%ld%lld", &n, &c); memset(used, 0, sizeof used); isprint = 0; isdown = 1; isfirst = 1; id = 1; while (n) { if (isdown) { if (c > g[n][id].down) { c -= g[n][id].down; if (isfirst) isdown = 0; else ++id; } else { SolveAndPrint(id, n--); isfirst = 0; isdown = 0; id = 1; } } else { if (c > g[n][id].up) { c -= g[n][id++].up; if (isfirst) isdown = 1; } else { SolveAndPrint(id, n--); isfirst = 0; isdown = 1; } } } putchar('\n'); } fclose(stdin); fclose(stdout); return 0; }