poj1037, A decorative fence, ceoi2002

解题报告见黑书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;

}

 

你可能感兴趣的:(poj)