poj 1037解题报告

Poj 1037是一道很好的动态规划的题目。这道题也是想了很久才Accept。 下面分享一下解题思路, 记录一下自己的思想历程, 同时如果有错误的话希望大家能给予指导。

一、题目大意

      题目的基本意思是这样的: 给定N个栅栏,这N栅栏的长度互不相同,1~N。N栅栏显然有很多不同的排列。在这些排列中,有一些排列被称为“cute”排列。这些排列有一下特点:

     1.这些栅栏的长度各不相同(这是显然的);

     2. 这些栅栏应当是起起伏伏的,也就是说某个栅栏,要么比它的“邻居”栅栏都矮,要么比它的邻居栅栏们都高。换句话说,如果第i个栅栏比第i - 1个栅栏矮的话,那么它一定要比第i + 1个栅栏高。

满足这些条件的栅栏叫做"cute"栅栏。

     在这些“cute”栅栏中,是有次序的。两个这样的“cute”栅栏从左往右数,第一个不一样的栅栏的高度小的那个次序在前。比如,栅栏群1324 就在栅栏1432之前,因为它们第一个不一样的栅栏3 < 4.

现在给你两个数,N 和 C。 N表示的栅栏的总数,C表示的是第多少个“cute”栅栏。要求输出这个栅栏的序列。

二、解题思路

      1.找出所有满足条件的栅栏的数目

             N个栅栏的全排列总共有N!中,那么在这些全排列中,满足条件的全排列有多少呢?

            我们首先定义两个辅助数组:downArray[N][N] 和upArray[N][N]。这个辅助数组的意思如下:downArray[i][j]表示一共有i个栅栏,在这些栅栏组成的全排列中,以第j个栅栏作为起始,并且第二个栅栏比第一个栅栏矮的那些排列的数目;upArray[i][j] 表示一共有i个栅栏,在这些栅栏组成的全排列中,以第j个栅栏作为起始,并且第二个栅栏比第一个栅栏高的哪些排列的数目;

            这些数组元素之间有什么关系呢?

            首先我们举个例子,加入第一个栅栏的高度是N的话,因为downArray这个数组需要第二个元素比第一个元素要矮,所以第二个栅栏的高度只可能N-1,N-2,。。。。1,而且为了满足条件,第三个栅栏必须比第二个栅栏要矮,也就是说对于第二根栅栏来说,后面的N-1个排列的个数等于以 第二根栅栏作为起始,并且栅栏的数目为N-1的upArray的值,  所以我们下面的公式:

            downArray[i][j] = sum{upArray[i - 1][j - 1],upArray[j - 1][j - 2] ,.........upArray[i - 1][1] };

            同理有 upArray[i][j] = sum{downArray[i - 1][j + 1],downArray[i - 1][j + 2]............downArray[N]};

       我们注意到 sum{upArray[i - 1][j - 2], .....upArray[i - 1][1]} = downArray[i][j - 1],

       所以1式可以简化为 downArray[i][j] = upArray[i - 1][j - ] + downArray[i][j - 1],

       另外,我们可以注意到另外一层关系,以N开头的downArray的数目,实际上等于以1开头的upArray的数目。那么2式可以简化为:

        upArray[i][j] = downArray[i][i - j + 1]

       得到这两个关系式,并且注意到upArray[1][1] = downArray[1][1] = 1

       我们就可以得到所有满足条件的数目了。


   2. 找出第C个满足条件的栅栏

        下面的问题就变成了怎样找到第C个满足条件的栅栏。

         注意到以第j个栅栏为起始的满足条件的总共栅栏数目等于upArray[N][j] + downArray[N][j]

         并且downArray[N][j] 的次序是在upArray[N][j]之前的。我们先考察upArray[N][1]( 不考虑downArray[N][1]是因为它等于0),如果C小于upArray[N][1],说明第一根栅栏的高度是2,接着去考察第二根栅栏,并且这时候一定要考察第二根栅栏的downArray(满足起起伏伏的条件)。如果C大于的话,用C减去upArray[N][1],接着去考察downArray[2]和upArray[2]

         如果C小于downArray[N][2],说明第一根栅栏以2开头,接下来确定第二根栅栏,注意只需要考察第二根栅栏的upArray就可以了(满足起起伏伏条件);如果C大于downArray[N][2]的话,用C减去downArray[N][2],接着考察upArray[N][2],如果小于的话,说明第一个栅栏高度应该为2,并且应当考察第二根栅栏的downArray;

        如果C小于downArray[N][2] + upArray[N][2],说明第一个栅栏的高度不等于2,接下来应该从3开始,直到可以确定第一个栅栏的高度,再一次确定其余的栅栏高度。




接下来贴上Accept的代码:

#include <iostream>
#include <stdio.h>

using namespace std;

__int64 upArray[21][21];
__int64 downArray[21][21];
int number[21];
int N[101];
__int64 C[101];

void GetNumberC(int N,__int64 C)
{
	bool up;
	int findindex;
	for(int i = 1;i <= N;i++)
	{
		number[i] = i; 
	}
	for(int i = N;i >= 1;i--)
	{
		if(i == N)
		{
			for(int j = 1;j <= i;j++)
			{
				if(C - downArray[i][j] <= 0)
				{
					findindex = j;
					up = true;
					break;
				}
				else
				{
					C -= downArray[i][j];
				}

				if(C - upArray[i][j] <= 0)
				{
					findindex = j;
					up = false;
					break;
				}
				else
				{
					C -= upArray[i][j];
				}
			}
			printf("%d ",number[findindex]);
			for(int j = findindex;j <= i;j++)
			{
				number[j] = number[j + 1];
			}
		}
		else
		{
			if(up == true)
			{
				for(int j = 1;j < findindex;j++)
				{
					if(C - upArray[i][j] <= 0)
					{
						findindex = j;
						up = false;
						break;
					}
					else
					{
						C -= upArray[i][j];
					}
				}
				printf("%d ",number[findindex]);
				for(int j = findindex;j <= i;j++)
				{
					number[j] = number[j + 1];
				}
			}
			else
			{
				for(int j = findindex;j <= i;j++)
				{
					if(C - downArray[i][j] <= 0)
					{
						findindex = j;
						up = true;
						break;
					}
					else
					{
						C -= downArray[i][j];
					}
				}
				printf("%d ",number[findindex]);
				for(int j = findindex;j <= i;j++)
				{
					number[j] = number[j + 1];
				}
			}
		}
	}
	printf("\n");
}	

int main()
{
	int K,max = 0;
	scanf("%d",&K);
	for(int i = 1;i <= K;i++)
	{
		scanf("%d %I64d",&N[i],&C[i]);
	}

	downArray[1][1] = upArray[1][1] = 1; 
	for(int i = 1;i<= 20;i++)
	{
		for(int j = 2;j <= i;j++)
		{
			downArray[i][j] = downArray[i][j - 1] + upArray[i - 1][j - 1];
		}
		for(int j = 1;j <= i; j++)
		{
			upArray[i][j] = downArray[i][i - j + 1];
		}
	}
	for(int i = 1;i <= K;i++)
	{
		GetNumberC(N[i], C[i]);
	}
	return 0;
}



         



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值