POJ 1037 A decorative fence DP

时空隧道


题目大意:
有n个栅栏,高度分别为1~n,要求求出第c个按照字典序排序的合法序列
合法序列就是波浪形的序列


分析:
感觉DP方程有一些难度… >_<
f[i][j][0]代表长度为i的合法序列并且以j作为开头数字,并且第二个数字大于第一个数字(也就是上升序列)
f[i][j][1]同理
这个DP的精华在于f数组的值与这i个栅栏的高度是没有关系的…
只和这i个栅栏的相对高度大小有关系…
那么也就是说当我们还剩下x个栅栏没有用的时候,这x个栅栏中以第k大的栅栏作为开头的dp值等同于前x个栅栏以k作为开头的dp值也就是f[x][k][0/1]…
那么转移就好想了…
f[i][j][0]=∑f[i-1][k][1](j<=k < i)
f[i][j][1]=∑f[i-1][k][0](1<=k < j)
然后就是如何求合法序列的问题了
我们枚举当前要填第i个数字,那么我们从1~n枚举未选过的数字,如果填上当前数字之后成为了上升序列,那么sum+f[n][cnt][0]cnt代表的是未选过的数字中当前枚举的数字是第几大的,反之+f[n][cnt][1]
当sum>=c的时候代表当前第i位填当前枚举数字(这个自己YY吧….)
然后c-=sum


代码如下:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define int long long
using namespace std;
const int maxn=20+5;
int cas,n,c,f[maxn][maxn][2],vis[maxn],ans[maxn];
signed main(void){
    memset(f,0,sizeof(f));
    f[1][1][0]=f[1][1][1]=1;
    for(int i=2;i<=20;i++)
        for(int j=1;j<=20;j++){
            for(int k=j;k<i;k++)
                f[i][j][0]+=f[i-1][k][1];
            for(int k=1;k<j;k++)
                f[i][j][1]+=f[i-1][k][0];
        }
    scanf("%lld",&cas);
    while(cas--){
        scanf("%lld%lld",&n,&c);
        int sum=0,tmp;
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;i++){
            int cnt=0,j;
            for(j=1;j<=n;j++){
                tmp=sum;
                if(!vis[j]){
                    cnt++;
                    if(i==1)
                        sum+=f[n][cnt][0]+f[n][cnt][1];
                    else if(j>ans[i-1]&&(i==2||ans[i-1]<ans[i-2]))
                        sum+=f[n-i+1][cnt][1];
                    else if(j<ans[i-1]&&(i==2||ans[i-1]>ans[i-2]))
                        sum+=f[n-i+1][cnt][0];
                    if(sum>=c)
                        break;
                }
            }
            vis[j]=1,ans[i]=j,sum=tmp;
        }
        for(int i=1;i<=n;i++)
            cout<<ans[i]<<" ";
        puts("");
    }
    return 0;   
}

by >_< neighthorn

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值