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;
}


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