poj 2411 (状态压缩dp)

//题意:一个n*m的矩形用 1* 2 的卡牌填满 问共有多少种方法
//方法:状态压缩+动规的思想 dp[i][j] 表示前i -1行全部填满 第i行状态为j的方法总和
//           状态J的二进制状态 0 表示该位被占用了 1 代表 该为是空的
//状态转移方程:dp[i][j] = sum(dp[i - 1][k])  (状态j可由状态k推出)
//起初有一个地方我有点模棱两可
//比如现在第i -1的状态 为 100111  它可以推导出 第i行的状态为 111111
//但是并不符合要求
//后来仔细想了想 111111 是可以由 100111推出来  但是实际上是由 (100111)的上一个状态推出来的
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <algorithm>
#include <iostream>
using namespace std;

long long dp[13][1<<11];
long long ans[15][15];
int n, m;
int isok(int s, int ss);
void init()
{
    int maxn = (1<<m) - 1;
    memset(dp, 0, sizeof dp);
    for(int i = 0; i <= maxn; i++)  //第一行的各个状态是由dp[0][maxn]推出来的
        if(isok(i, maxn))
            dp[1][i] += 1;
}
int isok(int s, int ss)
{
        for(int k = 1; k <= m; )
        {
            if(s & (1<<(k - 1))) //如果第i行第k列为1
            {
                if(ss & (1<<(k -1))) //如果第i-1行第k列为1
                {
                    //必须选择横放
                    if( (!(s & (1<<k))) || (!(ss &(1<<k))) || (k == m))  return 0; //如果 第i行第k+1列不为1|| 第i-1行第k+1列不为1 ||k已经在最后一位上 判定状态不合法
                    k += 2; // 该位合法 查看两位后
                }
                else k++; //该位合法 查看下一位
            }
            else{ //如果第i行第k列为0
                if( ! (ss & (1<<(k - 1)))) return 0;  //如果第i - 1行第k列为0  那么状态不合法
                k++; // 该位合法 查看下一位
            }
        }
        return 1;
}
int main()
{
    while(cin>>n>>m && (n != 0))
    {
        if( (n&1) && (m&1)) {
            cout<<0<<endl;
            continue;
        }
        if(ans[n][m]) {
            cout<<ans[n][m]<<endl;
            continue;
        }
        if(n < m)  swap(n, m);   //使m小从而使得状态尽可能少
        init();
        for(int i = 2; i <= n; i++) //模拟到第i行
            for(int j = 0; j < (1<<m); j++)  //模拟第i行状态j
                for(int k = 0; k < (1<<m); k++) //模拟第i-1行状态k
                    if(isok(j, k)) //如果状态k -> j合法
                        dp[i][j] += dp[i - 1][k];
        ans[n][m] = ans[m][n] = dp[n][(1<<m) - 1];
        cout<<ans[n][m]<<endl;
    }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章