Poj1037 A decorative fence(DP好题)

题目链接:http://poj.org/problem?id=1037
题意:给你N个板子,每个板子长度都不一样。长度为1~n,使板子排列成波浪形,即对于对于1< i < n的每个板子,满足第i个板子左右两边的木板都比它高或者低。输出第C个波浪形的序列是多少。
假如不考虑波浪形,那么我们可以知道第C个全排列是多少。
比如:
N=4,C=10;
如果第一个数为1,后面三个数就有3!种排法,C-3!=4>0,说明第一个数不是1,此时C=4.如果第一个数为2,C-3!=-2<=0,那么说明第一个数应该是2。C+3!=4,然后按照前面的方法推算出最后3个数的值。最后可以得到N=4时,第10种全排列的值是多少。
然而题目要求是波浪形的全排列。
我们假定dp[i][len][0]是长度为len的,以第i短的木棒开始,且前两个数是升序的全排列的个数。同理dp[i][len][1]是降序的全排列的个数。i不是长度为i
dp转移见下图
这里写图片描述
这里写图片描述
需要确定到底是升序还是降序开头。因此对于第一个数特殊处理一下。
之后出现的数需要判断是剩余的数中第几小。然后按照之前介绍的方法对全排列的每一位进行判断是多少。

#include <iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define LL long long
LL dp[25][25][2];//0->升序,1->降序;
int mark[25];//检测是否用过
int main()
{
    memset(dp,0,sizeof(dp));
    dp[1][1][0]=dp[1][1][1]=1;
    for(int len=2; len<=20; len++)
    {
        for(int i=1; i<=len; i++)//枚举第一根木棒长度
        {
            //枚举第二根木棒长度
            for(int j=1; j<i; j++)
                dp[i][len][0]+=dp[j][len-1][1];
            //枚举第二根木棒长度
            for(int j=i; j<len; j++)
                dp[i][len][1]+=dp[j][len-1][0];
        }
    }
    int T;
    cin>>T;
    while(T--)
    {
        int N;
        LL C;
        cin>>N>>C;
        memset(mark,0,sizeof(mark));
        int flag=0;
        int l=1,r=N;
        int j;
        for(int i=1; i<=N; i++)
        {
            if(flag)
                break;
            for(j=0; j<2; j++) //确定升序还是降序
            {
                C-=dp[i][N][j];
                if(C<=0)
                {
                    C+=dp[i][N][j];
                    cout<<i;
                    mark[i]=1;
                    if(j==0)
                        l=1,r=i-1;
                    else
                        l=i+1,r=N;
                    flag=1;
                    break;
                }
            }
        }
        j^=1;
        for(int len=N-1; len>=1; len--)
        {
            int num=0;
            for(int i=1;i<l;i++)
            {
                if(!mark[i])
                    num++;
            }
            for(int i=l; i<=r; i++)//i是剩余木棒中第num短的
            {
                if(!mark[i])
                {
                    num++;
                    C-=dp[num][len][j];
                    if(C<=0)
                    {
                        C+=dp[num][len][j];
                        mark[i]=1;
                        cout<<" "<<i;
                        if(j==0)
                            l=1,r=i-1;
                        else
                            l=i+1,r=N;
                        j^=1;
                        break;
                    }
                }
            }
        }
        cout<<endl;
    }
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值