POJ1037 A decorative fence

题目大意(转)

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



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值