【poj 1037】A decorative fence 排列dp

来源:互联网 发布:java微服务开发示例 编辑:程序博客网 时间:2024/05/29 07:50

没事做,就好好来说一下这一道题把。

首先明确这里的dp定义,但是在这里我和我的队友们产生了分歧,虽然效果和解题方法都是一样的但是dp嘛,重要的是在于对于思想的理解。

我的想法(也是xgtao的):定义f[i][j]表示用1->i组成排列,且最后一个数字是j的方案数,但是我们可以发现,如果仅仅是这样的话,那么将会有很多的情况没法表示,例如:1 4 2 3 在前3个数字的时候,1 4 2并不是连续的自然数,所以就不能用刚才说的方法表示。乍一看的确如此,可是我们仔细想一下,其实因为是排列,每一个数只会出现一次1 4 2的大小关系其实等价于1 3 2,也就是说,这两种表示方法的方案数其实是一样的,现在我们再填入一个数字3就把3前面所有大于等于3的数全部加上1(大小关系不变,方案数依旧不变,全部加一只是我们自行脑补的内容,保证合法),这样只要保证在枚举的时候保证前面比他小就一定能保证合法

而他们则认为这是直接的表示前i个数最后一个数是前面的第j大例如 1 2 4   4是第3大所以表示成f[3][3]

怎么好理解怎么想吧,无所谓。

#include<cstdio>#include<cstring>#include<iostream>#define LL long longusing namespace std;LL n,c,vis[35],ans[35];LL f[35][35][2];//0 下降 1上升void init(){    f[1][1][0]=f[1][1][1]=1;    for(LL i=2;i<=25;i++)        for(LL j=1;j<=i;j++){            for(LL k=1;k<j;k++)f[i][j][1]+=f[i-1][k][0];            for(LL k=j;k<i;k++)f[i][j][0]+=f[i-1][k][1];//只能枚举到i-1 和他相等的话,可以理解将和他相等的数佳佳        }}void solve(){    memset(vis,0,sizeof(vis));    memset(ans,0,sizeof(ans));        for(LL i=1;i<=n;i++){        LL temp,cnt=0,d,now,j;        for(j=1;j<=n;j++){            temp=c;            if(vis[j])continue;            cnt++;now=n-i+1;            if(i>2)d= ans[i-2]<ans[i-1] ? 0 : 1;            if(i==1)c-=f[now][cnt][1]+f[now][cnt][0];            else if(i==2){                if(j>ans[1])c-=f[now][cnt][1];                else c-=f[now][cnt][0];            }else{                if(d==0&&j<ans[i-1])c-=f[now][cnt][0];                else if(d==1&&j>ans[i-1])c-=f[now][cnt][1];            }            if(c<=0)break;        }        ans[i]=j;vis[j]=1;        c=temp;    }    for(LL i=1;i<=n;i++)printf("%lld%c",ans[i],i==n ? '\n' : ' ');}int main(){    init();    LL T;scanf("%lld",&T);    while(T--){        scanf("%lld%lld",&n,&c);        solve();    }    return 0;}


0 1