【POJ2663Tri Tiling】状态压缩数位dp/矩阵状态转移

Tri Tiling
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 7683 Accepted: 4041

Description

In how many ways can you tile a 3xn rectangle with 2x1 dominoes? 
Here is a sample tiling of a 3x12 rectangle. 

Input

Input consists of several test cases followed by a line containing -1. Each test case is a line containing an integer 0 <= n <= 30.

Output

For each test case, output one integer number giving the number of possible tilings.

Sample Input

2
8
12
-1

Sample Output

3
153
2131

Source


对于当前列的状态表示成长度为3的01串之后,枚举可以得到下一个状态的情况,构造操作矩阵,然后对初始矩阵从000出发(所以该值为1)

    构造一个8 * 8 (2^3)的矩阵  注意枚举仅处理当前列的状态,不然对于下一列的状态会重复计算




#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
struct Matrix
{
    int m[9][9];
} E, D;
void init()
{
    for (int i = 1; i <= 8; i++)
        for (int j = 1; j <= 8; j++)
        {
            E.m[i][j] = (i == j);
            D.m[i][j] = 0;
        }

    D.m[1][2] = 1;
    D.m[1][5] = 1;
    D.m[1][8] = 1;

    D.m[2][1] = 1;
    D.m[2][7] = 1;

    D.m[3][6] = 1;

    D.m[4][5] = 1;

    D.m[5][1] = 1;
    D.m[5][4] = 1;

    D.m[6][3] = 1;

    D.m[7][2] = 1;

    D.m[8][1] = 1;
}
Matrix Multi(Matrix A, Matrix B)
{
    Matrix ans;
    for (int i = 1; i <= 8; i++)
        for (int j = 1; j <= 8; j++)
        {
            ans.m[i][j] = 0;
            for (int k = 1; k <= 8; k++)
                ans.m[i][j] += A.m[i][k] * B.m[k][j];
        }
    return ans;
}
Matrix Pow(Matrix A, int k)
{
    Matrix ans = E;
    while (k)
    {
        if (k & 1)
        {
            k--;
            ans = Multi(ans, A);
        }
        else
        {
            k /= 2;
            A = Multi(A, A);
        }
    }
    return ans;
}
int main()
{
    // freopen("C:\\Users\\Sky\\Desktop\\1.in","r",stdin);
    int n;
    init();
    while (scanf("%d", &n) != EOF)
    {
        if (n == -1)
            break;
        Matrix cnt = Pow(D, n);
        /*
        for(int i=1;i<=8;i++)
        {
            for(int j=1;j<=8;j++)
                printf("%d ",cnt.m[i][j]);
            printf("\n");
        }
        */
        Matrix tmp;
        tmp.m[1][1] = 1;
        for (int i = 2; i <= 8; i++)
            tmp.m[i][1] = 0;
        Matrix ans;
        for (int i = 1; i <= 8; i++)
            for (int j = 1; j <= 1; j++)
            {
                ans.m[i][j] = 0;
                for (int k = 1; k <= 8; k++)
                    ans.m[i][j] += cnt.m[i][k] * tmp.m[k][j];
            }
        printf("%d\n", ans.m[1][1]);
    }
    return 0;
}

dp[i][j]表示前i-1行全部都填满的时候j对应的状态(如下)

/*
0 -- 000
1 -- 001
2 -- 010
3 -- 011
4 -- 100
5 -- 101
6 -- 110
7 -- 111
*/

然后因为情况数很少,所以就一一枚举出来了

状态转移方程:

dp[i][1] = dp[i-1][6];
dp[i][2] = dp[i-1][5];
dp[i][4] = dp[i-1][3];
dp[i][3] = dp[i-1][7] + dp[i-1][4];
dp[i][5] = dp[i-1][2];
dp[i][6] = dp[i-1][7] + dp[i-1][1];
dp[i][7] = dp[i][1] + dp[i][4] + dp[i-2][7];

0-》7

直接转移


                        #define DeBUG
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
#include <string>
#include <set>
#include <sstream>
#include <map>
#include <list>
#include <bitset>
using namespace std ;
#define zero {0}
#define INF 0x3f3f3f3f
#define EPS 1e-6
#define TRUE true
#define FALSE false
typedef long long LL;
const double PI = acos(-1.0);
//#pragma comment(linker, "/STACK:102400000,102400000")
inline int sgn(double x){return fabs(x) < EPS ? 0 :(x < 0 ? -1 : 1);}
#define N 100005
int dp[31][8];
int main()
{    
    #ifdef DeBUGs
        freopen("C:\\Users\\Sky\\Desktop\\1.in","r",stdin);
    #endif
    memset(dp,0,sizeof(dp));
    dp[0][0]=1;
    for(int i=1;i<=30;i++)
    {
        dp[i][0]=dp[i-1][1]+dp[i-1][4]+dp[i-1][7];
        dp[i][1]=dp[i-1][0]+dp[i-1][6];
        dp[i][2]=dp[i-1][5];
        dp[i][3]=dp[i-1][4];
        dp[i][4]=dp[i-1][3]+dp[i-1][0];
        dp[i][5]=dp[i-1][2];
        dp[i][6]=dp[i-1][1];
        dp[i][7]=dp[i-1][0];
    }
    int n;
    while(scanf("%d", &n),n+1)
    {
        printf("%d\n", dp[n][0]);
    }
    
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值