UVALive 6625_状态压缩

来源:互联网 发布:vmware mac os 10.9 编辑:程序博客网 时间:2024/05/22 19:57
题意:

给你k行方格,每一行的方格数不同,下面一行的方格数小于等于上面的方格数,现往方格里填数字,要求同一行的方格,左边方格中的值要小于等于右边方格中的值,同一列中,下面的值要严格大于上面的值,问取值范围在[1,N]的范围内,满足要求的方法数。

思路:

这道题肯定首先是想到的状压DP,但如果从行开始递推的话,会发现十分困难,因为在同一行中数字可以有重复的(左边的小于等于有边的),所以状态压缩的时候很难用一个数字表示一列方格中的取值情况,而对于一列来说,因为下面的数字肯定是大于上面的数字,所以每个数字要么出现一次要么不出现,就可以用一个二进制位来表示,而一列对多有7个方格,所以可以用一个7位二进制数来表示一列是状态。

如此,用dp[i][j]表示到i列状态为j时的方法数,那么如果对于i+1列的某个状态k,能由第i列的状态j推得,那么可以推得状态转移方程:

dp[i+1][k]=dp[i+1][k]+dp[i][j]

代码:

#include <iostream>#include <cstdio>#include <cstring>using namespace std;int rows[8];int col[8];long long dp[8][1<<12];int k;int N;void cal(){    memset(col,0,sizeof(col));    for(int i=1;i<=rows[1];i++){        col[i]=0;        for(int j=1;j<=k;j++){            if(rows[j]>=i){                col[i]++;            }        }    }}int cal_bits(int s){    int ret=0;    while(s){        ret++;        s-=s&(-s);    }    return ret;}bool judge(int a,int b){    int s1[12],s2[12];    int c1=0,c2=0;    for(int i=0;i<N;i++){        if(1<<i&a) s1[++c1]=i;    }    for(int i=0;i<N;i++){        if(1<<i&b) s2[++c2]=i;    }    for(int i=1;i<=c2;i++){        if(s1[i]>s2[i])            return false;    }    return true;}int main(){    while(~scanf("%d",&k)){        memset(dp,0,sizeof(dp));        for(int i=1;i<=k;i++){            scanf("%d",&rows[i]);        }        scanf("%d",&N);        cal();        int s=1<<N;        for(int i=0;i<s;i++){            if(cal_bits(i)==col[1])                dp[1][i]=1;        }        for(int i=1;i<=rows[1];i++){            for(int j=0;j<s;j++){                if(cal_bits(j)==col[i]&&dp[i][j]){                    for(int p=0;p<s;p++){                        if(cal_bits(p)==col[i+1]&&judge(j,p)){                            dp[i+1][p]+=dp[i][j];                        }                    }                }            }        }        long long ans=0;        for(int i=0;i<=s;i++){            ans+=dp[rows[1]][i];        }        cout<<ans<<endl;    }    return 0;}

0 0