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;
}