JOJ 2190: Mondriaan's Dream (状态压缩DP +DFS)

在网上找了很多解题报告,看了很久才理解,不过理解之后代码还是很好实现的><

还是对每个格2种状态 ,1表示被占 , 0表示不被占 ,

对每一行可以做横放 , 竖放 , 不放, 3种操作, 没种操作都要求出对应上一行的状态, 比如若竖放 , 上一行的状态就必须是0 , 既没被占用,横放和不放对应的上一行必须是1,这样才能将所有的格子都填满,其次对第一行只有横放和不放2种操作, 对状态的转换自己YY下就可以出来,对于状态的转换,是树状的, 所以用DFS更好理解

#include <cstdio>
#include <cstring>

typedef long long ll;
const int maxn=1<<12;

ll dp[13][maxn];
int n,m;

void dfs(int c , int opt)//only horizontal lie and not lie in first row;
{
    if(c>=m)
    {
        if(c==m)dp[0][opt]++;
        return;
    }
    dfs(c+1 , opt<<1);
    dfs(c+2 , opt<<2|3);
}

void DFS(int r , int c , int pre , int opt)
//这里的pre并非要对上一行处理,而是根据本行的状态求出上一行对应状态,以便更新DP[][] 
{
    if(c>=m)
    {
        if(c==m)dp[r][opt]+=dp[r-1][pre];
        return ;
    }
    DFS(r , c+1 , pre<<1 , opt<<1|1);//vetical
    DFS(r , c+2 , pre<<2|3 , opt<<2|3);//horizoncal
    DFS(r , c+1 , pre<<1|1 , opt<<1);//
}
void debug ()
{
    for (int i=0 ; i<n ; ++i)
    {
        for (int j=0 ; j<(1<<m) ; ++j)
        {
            printf("j=%o %d ",j,dp[i][j]);
        }
        puts("");
    }
}
int main ()
{
    int cas;
    scanf("%d",&cas);
    while (cas--)
    {
        scanf("%d%d",&n,&m);
        memset (dp , 0 , sizeof(dp));
        if((n&1) && (m&1)){printf("0\n"); continue ;}
        if(m>n)n^=m^=n^=m;//行是指数级 , 列式线性的 
        dfs(0,0);
        for (int i=1 ; i<n ; ++i)
            DFS(i , 0 , 0 , 0);
        ll ans=0;
        /*for (int i=0 ; i<(1<<m) ; ++i)
        {
            ans+=dp[n-1][i];
        }*/
        //debug ();
        //printf("%lld\n",ans);
        //本来以为sigma(dp[n-1][i])是答案,debug的时候发现最后一行全部放满的dp[n-1][(1<<m)-1]才是答案 
        printf("%lld\n",dp[n-1][(1<<m)-1]);
    }
    return 0;
}


你可能感兴趣的:(JOJ 2190: Mondriaan's Dream (状态压缩DP +DFS))