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