POJ 1037 A decorative fence DP

来源:互联网 发布:四海认证淘宝渔具 编辑:程序博客网 时间:2024/06/05 08:45

时空隧道


题目大意:
有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 longusing 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 0
原创粉丝点击