题目大意(转)
有N条木板,长度不同,分别为1,2,……,N单位长度。现在要将这N条木板排成可爱的形状,所谓可爱的形状,是指对于不在边缘的木板,两边相邻的木板要么都比它高,要么都比它低,成波浪形排列。
N条木板有若干种可爱的排列方法,现将这些排列按字典排序,即第一条木板较短的排前面,若第一条木板相同,则第二条木板相同的排前面,以此类推;最后,从1开始对排列方法编号。
现输入木板条数N和编号C,要求按顺序输出这N条木板的长度(长度分别为1到N)
题目分析:(转)
其实刘汝佳的那本书上p257有这个例题,也有很详细的解题报告。
假设从1···N中除去数A1,A2···AK后剩余B1,B2,Bn-k。那么求以A1,A2···Ak+1为前K+I项的
栅栏数目就等于求以B1,B2,····Bn-k为后n-k项,头两块木块高度上升(如果A(k-1)>AK)或者
下降( A(k-1)<Ak ).的栅栏数目。又因为所谓的“高低起伏”只是木头与木头高度之间的相对大小关系,而
与木头的绝对高度无关,所以倘若A(k+1)是B(1,2,····n-k)中第T小的数,那么求以B1,B2··
B(n-k )为后n-k项,头两块木块高度上升(或下降)的栅栏数量就等同于求以1,2···n-k为后n-k项.
头一木块高度为T,头两木块高度上升(或者下降)的栅栏数量up[ n-k ][ T ]. ( 或者down[ n-k ][ T ])。
可以得到如下状态转移方程:
up[n][T=sum{ down[n-1][ i ] (i=T---->n-1)}
down[n][T]=sum { up[n-1][i(i=1--->T-1) }
//My:
上面那个式子是成立的。因为:书上说是理所当然。我是费时间想了的。所以写下:
比如有5个木块:求第一个为3的上升的木块有多少根,即dp[5][3]...那么剩下的长度为1 2 4 5映射到1 2 3 4
。那么会发现,所有比3大的数,映射的数是大于等于3的,(因为他们前面少了一个嘛)。所以要从3累加到4。 同样的可以分析down的情况。
up[n][k] 长度为n以k开始的,前两个数呈上升趋势的数列的个数
down[n][k] 长度为n以k开始的,前两个数呈下降趋势的数列的个数
up[n][k] = sigma(down[n-1][i]) k<=i<=n-1
down[n][k]= sigma(up[n-1][i]) 1<=i<=k-1
第一个满足 sigma(up[n][i]+down[n][i])>=c 的i即为第一位a1
c1=c-(up[n][a1]+down[n][a1])
第一个满足 sigma(up[n-1][i])>=c1 1<=i<a1, sigma(down[n-1][i])>=c1 a1<=i<=n-1 为a2
一旦确定a1,a2就可以与确定下面是up还是down,两者交替.
for (long long i = 1; i < n; i++)
if (ans[i] >= t)
ans[i]++;
算法中的这三行非常精妙,虽然ans[n]已经使用,但是却不影响g[][]高低计数的特征,最后只需作如下处理:大于ans[n]的直接加1,以起到忽视掉选中的高度为t的效果,不影响局部正确性。递归之后,自然也不影响整体正确性
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#include<memory.h>
#include<queue>
#include<stack>
using namespace std;
#define maxn 30
struct Node
{
long long up, down;
} g[maxn][maxn];
long long n;
long long c;
long long ans[maxn];
void getfirst(long long n, long long c, bool u)
{
if (n == 0)
return;
long long t;
long long sum;
if (!u)
{
sum = 0;
t = ans[n + 1];
while (sum + g[n][t].down < c)
{
sum += g[n][t].down;
t++;
}
}
else
{
sum = 0;
t = 1;
while (sum + g[n][t].up < c)
{
sum += g[n][t].up;
t++;
}
}
ans[n] = t;
getfirst(n - 1, c - sum, !u);
for (long long i = 1; i < n; i++)
if (ans[i] >= t)
ans[i]++;
}
int main()
{
long long i,j,k;
g[1][1].up = 1;
g[1][1].down = 1;
for (i = 2; i <= 20; i++)
{
for ( j = 1; j <= i; j++)
{
g[i][j].up = g[i][j].down = 0;
for ( k = j; k <= i - 1; k++)
g[i][j].up += g[i - 1][k].down;
for (k = 1; k <= j - 1; k++)
g[i][j].down += g[i - 1][k].up;
}
}
long long x;
scanf("%lld", &x);
while (x--)
{
scanf("%lld%lld", &n, &c);
long long t = 1, sum = 0;
while (sum + g[n][t].down + g[n][t].up < c)
{
sum += g[n][t].down + g[n][t].up;
t++;
}
ans[n] = t;
if (sum + g[n][t].down < c)
getfirst(n - 1, c - sum - g[n][t].down, false);
else
getfirst(n - 1, c - sum, true);
for (i = 1; i < n; i++)
if (ans[i] >= t)
ans[i]++;
printf("%lld", ans[n]);
for (i = n - 1; i >= 1; i--)
printf(" %lld", ans[i]);
putchar('\n');
}
return 0;
}