http://poj.org/problem?id=3420

简单版:http://poj.org/problem?id=2663(题解:https://blog.csdn.net/weixin_43272781/article/details/88983956

题意:在4*N的方格内塞满规格是1*2的砖头,横放竖放都可以,问一共有多少种放法。

C++版本一

/*
*@Author:   STZG
*@Language: C++
*/
//#include <bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<bitset>
#include<queue>
#include<deque>
#include<stack>
#include<cmath>
#include<list>
#include<map>
#include<set>
//#define DEBUG
#define RI register int
#define endl "\n"
using namespace std;
typedef long long ll;
//typedef __int128 lll;
const int N=100000+10;
const int M=100000+10;
const int MOD=1e9+7;
const double PI = acos(-1.0);
const double EXP = 1E-8;
const int INF = 0x3f3f3f3f;
int t,n,m,k,p,l,r,u,v;
int ans,cnt,flag,temp,sum;
int s[25][25];
int b[25][25];
int a[25][25];
char str;
struct node{};
void dfs(int x,int now,int pre) {
    if (x==4) {++s[pre][now];return;}
    dfs(x+1,now<<1|1,pre<<1);
    dfs(x+1,now<<1,pre<<1|1);
    if (x<3) dfs(x+2,now<<2,pre<<2);
}
void Matrix(int a[25][25],int b[25][25]){
    int c[25][25];
    memset(c,0,sizeof(c));
    for(int i=0;i<16;i++){
        for(int j=0;j<16;j++){
            for(int k=0;k<16;k++){
                c[i][j]=(c[i][j]+a[i][k]*b[k][j])%m;
            }
        }
    }
    for(int i=0;i<16;i++){
        for(int j=0;j<16;j++){
            a[i][j]=c[i][j];
        }
    }
}
int power(int k){
    for(int i=0;i<16;i++){
        for(int j=0;j<16;j++){
            a[i][j]=(i==j);
            b[i][j]=s[i][j];
        }
    }
    while(k){
        if(k&1)Matrix(a,b);
        Matrix(b,b);
        k>>=1;
    }
    return a[0][0];
}
int main()
{
#ifdef DEBUG
	freopen("input.in", "r", stdin);
	//freopen("output.out", "w", stdout);
#endif
    //ios::sync_with_stdio(false);
    //cin.tie(0);
    //cout.tie(0);
    //
    dfs(0,0,0);
    while(~scanf("%d%d",&n,&m)&&n+m){
        cout<<power(n)<<endl;
    }

#ifdef DEBUG
	printf("Time cost : %lf s\n",(double)clock()/CLOCKS_PER_SEC);
#endif
    //cout << "Hello world!" << endl;
    return 0;
}

C++版本二

题解: 

递推式:a[i]=a[i-1]+5*a[i-2]+a[i-3]-a[i-4];

由于N高达10^9,所以要用矩阵进行优化。 
|0 1 0 0| 
|0 0 1 0| 
|0 0 0 1| 
|-1 1 5 1| 
与 
|a[i-3]| 
|a[i-2]| 
|a[i-1]| 
|a[i]| 
相乘

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <cstdlib>
#include <algorithm>
#define LL long long

using namespace std;
const int  Max = 10;

int Mod;
struct Matrix
{
    int n,m;
    int a[Max][Max];
    void clear()//清空矩阵
    {
        n=0;
        m=0;
        memset(a,0,sizeof(a));
    }
    Matrix operator * (const Matrix &b)const//矩阵相乘
    {
        Matrix tmp;
        tmp.clear();
        tmp.n=n;
        tmp.m=b.m;
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<b.m;j++)
            {
                for(int k=0;k<m;k++)
                {
                    tmp.a[i][j]=(tmp.a[i][j]+(a[i][k]%Mod)*(b.a[k][j]%Mod))%Mod;
                }
            }
        }
        return tmp;
    }
};



void Pow(int m)
{
    Matrix s;
    s.clear();
    s.n=4;
    s.m=4;
    s.a[3][3]=1;s.a[3][2]=5;
    s.a[3][1]=1;s.a[3][0]=-1;
    s.a[1][2]=1;s.a[2][3]=1;
    s.a[0][1]=1;

    Matrix ans;
    ans.clear();
    ans.n=4;
    ans.m=1;
    ans.a[0][0]=1;
    ans.a[1][0]=5;
    ans.a[2][0]=11;
    ans.a[3][0]=36;
    while(m)//快速幂
    {
        if(m&1)
        {
            ans=s*ans;
        }
        s=s*s;
        m>>=1;
    }
    printf("%d\n",ans.a[3][0]);
}

int main()
{ 
    int n;
    while(scanf("%d %d",&n,&Mod),n)
    {
        if(n<4)
        {
            switch(n)
            {
                case 1:
                    printf("%d\n",1%Mod);
                    break;
                case 2:
                    printf("%d\n",5%Mod);
                    break;
                case 3:
                    printf("%d\n",11%Mod);
                    break;
            }
            continue;
        }
        Pow(n-4);
    }
    return 0;
}

C++版本三

题解:构造矩阵 DFS 矩阵快速幂

数据很大,不能直接搞了,我们矩乘一下。0表示已放置,1表示未放置。dfs跑出一个16∗16的转移矩阵,然后矩乘,最后输出ans[0][0]就可以了。

参考文章:原博客

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define HAS 4001
#define inf 2147483640
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
 
int n,m;
LL a[16][16],tmp[16][16],t[16][16],ans[16][16];
 
void dfs(int x,int now,int pre) {
    if (x==4) {++a[pre][now];return;}
    dfs(x+1,now<<1|1,pre<<1);
    dfs(x+1,now<<1,pre<<1|1);
    if (x<3) dfs(x+2,now<<2,pre<<2);
}
void power() {
    for (int i=0;i<16;i++) {
        for (int j=0;j<16;j++) t[i][j]=a[i][j],ans[i][j]=0;
        ans[i][i]=1;
    }
    while (n) {
        if (n&1) {
            for (int i=0;i<16;i++)
                for (int j=0;j<16;j++) {
                    tmp[i][j]=0;
                    for (int k=0;k<16;k++) (tmp[i][j]+=ans[i][k]*t[k][j]%m)%=m;
                }
            for (int i=0;i<16;i++)
                for (int j=0;j<16;j++) ans[i][j]=tmp[i][j];
        }
        n>>=1;
        for (int i=0;i<16;i++)
            for (int j=0;j<16;j++) {
                tmp[i][j]=0;
                for (int k=0;k<16;k++) (tmp[i][j]+=t[i][k]*t[k][j]%m)%=m;
            }
        for (int i=0;i<16;i++)
            for (int j=0;j<16;j++) t[i][j]=tmp[i][j];
    }
}
int main() {
    dfs(0,0,0);
    while (scanf("%d%d",&n,&m)!=EOF && n && m) {
        power();
        printf("%lld\n",ans[0][0]);
    }
    return 0;
}

C++版本四

题解:

设f(n,state):第n列铺砖状态为state时的情况数,状态state可以是:(0代表还未铺砖,1代表已被砖占据):
 state 0:0
         0
         0
         0
 state 1:1
         1
         0
         0
或者     
         0
         0
         1
         1
 state 2:
         1
         0
         0
         1
 state 3:
         0
         1
         1
         0
 state 4:
         1
         1
         1
         1
需要用到的情况就这么多,为什么呢,举个例子
先写出四行三列的格子,其中定义状态:
0      1     2     3    ...   14     15
X00   XX0   X00   XX0         X00    XX0
X00   X00   XX0   XX0         XX0    XX0
X00   X00   X00   X00         XX0    XX0
X00   X00   X00   X00         XX0    XX0
为考虑第2列的所有状态,其中x代表已被填满,0代表还是空的
再定义:
 0      1      2      3     ...   14     15
XX0    XXX    XX0    XXX          XX0    XXX
XX0    XX0    XXX    XXX          XXX    XXX
XX0    XX0    XX0    XX0          XXX    XXX
XX0    XX0    XX0    XX0          XXX    XXX

为考虑第三列的所有状态

那么第三列中状态0可以由第2列中的哪些状态转化而来呢?显然第2列的状态0,3,9,12,15可以通过铺砖得到第三列的状态0;
而计算状态0转移过程所需要用到的状态0,3,9,12,15可以由那些状态转移得到呢?除了状态9需要用到前一列的状态0和6得到,其他状态都可以互推,不需要新的状态。
那么需要用到的状态就是一开始所说的那几种,并且转移方程可以写成如下递推式:
f(n+1, 0) = f(n, 0) + 2 * f(n, 1) + f(n, 2) + f(n, 4)
 f(n+1, 1) = f(n, 0) + f(n, 1)
 f(n+1, 2) = f(n, 0) + f(n, 3)
 f(n+1, 3) = f(n, 2)
 f(n+1, 4) = f(n, 0)
写成矩阵的形式:(f(n+1,0),f(n+1,1),f(n+1,2),f(n+1,3),f(n+1,4))=A*(f(n,0),f(n,1),f(n,2),f(n,3),f(n,4));
A=1 1 1 0 1
  2 1 0 0 0 
  1 0 0 1 0
  0 0 1 0 0
  1 0 0 0 0
B=A^N,B[0][0]即是第N列砖的铺砖的所有可能数。
#define _CRT_SECURE_NO_DEPRECATE
#include<iostream>
#include<vector>
#include<set>
#include<algorithm>
#include<queue>
using namespace std;
typedef vector<int> vec;
typedef vector<vec> mat;
typedef long long ll;
const int M_MAX = 100000;
mat A, B;
ll n;
int N, M;
mat mul(mat &A,mat &B,int &M) {
    mat C(A.size(),vec(B[0].size()));
    for (int i = 0; i < A.size();i++) {
        for (int k = 0; k < B.size();k++) {
            for (int j = 0; j < B[0].size();j++) {
                C[i][j] = (C[i][j]+A[i][k]*B[k][j]) % M;
            }
        }
    }
    return C;
}

mat pow(mat A,ll n){
    mat B(A.size(),vec(A[0].size()));
    for (int i = 0; i < A.size();i++) {
        B[i][i] = 1;
    }
    while (n>0) {
        if (n & 1)B = mul(B, A,M);
        A = mul(A, A,M);
        n >>= 1;
    }
    return B;
}

int main() {
    while (scanf("%d%d",&N,&M)!=EOF&&N) {
        mat A(5,vec(5));
    
        A[0][0] =A[0][1]=A[0][2]=A[0][4]=1;
        A[1][0] = 2; A[1][1] = 1;
        A[2][0] = A[2][3] = 1;
        A[3][2] = 1;
        A[4][0] = 1;
        A = pow(A, N);
        printf("%d\n",A[0][0]);
    }
    return 0;
}