HihoCoder - 1151 骨牌覆盖问题·二 (矩阵快速幂 或 公式(很难想0.0 附详细推导步骤))

本文介绍了3xN骨牌覆盖问题,讨论了奇数长度无法覆盖,偶数长度的覆盖方式,并重点解析了矩阵快速幂和递推公式两种解题方法。给出了从状态转移矩阵到递推公式的详细推导过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:

https://hihocoder.com/problemset/problem/1151

题目:

时间限制:10000ms

单点时限:1000ms

内存限制:256MB

描述

上一周我们研究了2xN的骨牌问题,这一周我们不妨加大一下难度,研究一下3xN的骨牌问题?
所以我们的题目是:对于3xN的棋盘,使用1x2的骨牌去覆盖一共有多少种不同的覆盖方法呢?
首先我们可以肯定,奇数长度一定是没有办法覆盖的;对于偶数长度,比如2,4,我们有下面几种覆盖方式:

 

提示:3xN骨牌覆盖

输入

第1行:1个整数N。表示棋盘长度。1≤N≤100,000,000

输出

第1行:1个整数,表示覆盖方案数 MOD 12357

样例输入

62247088

样例输出

4037

题目大意:

嗯~中文题。。略过这一步0.0

题目分析:

重头戏来啦0.0  这道题其实是个系列题,从最简单的2*n的面积,到这道题的3*n的面积,后面还有k*n的面积的升级版。挺有意思的题目0.0

那么,我们应该怎么去解决这个问题呢?有两种方法这里会逐一去分析,第一种是矩阵快速幂,第二种代码简单但难想,速度也慢,就是递推公式的方法了(其实原题自带的题解就解释了矩阵快速幂的方法啦)(矩阵快速幂是正解0.0)


一、矩阵快速幂(图片来自题目自带的题解)

假设我们已经放好了一些骨牌,对于当前最后一列(第i列)骨牌,可能有8种情况,对于上面的这8种状态,我们用数字来标记它们。以放置骨牌的格子为1,未放置为0,转化为2进制数,进行状态压缩,可以得到以下状态:

注意,在讨论第i行的状态的时候,第i-1行一定是装满了的

这种情况看似是从状态1变成了状态0,其实是不对的。它不满足我们约定的放置方法,本质是第i行的状态1变成了第i行的状态7,而实际上我们应该放置的是第i+1行

在这种规则之下,我们就可以判断从 i 列的 某幢状态 能否转移为 第 i+1 列的状态

通过枚举8种状态到8种状态的转移,我们可以得到一个8x8的矩阵M(空白的地方均为0):

m[i][j]表示从状态i变成状态j的方案数。

然后我们就可以用A * M^n所求出的结果表示第n行各种状态的方案数。(其中A应该初始化为{0, 0, 0, 0, 0, 0, 0, 1}),而对于我们寻求的答案,自然也是第n行放置为状态7的方案数了。然后就是简单粗暴的矩阵快速幂。


二、递推公式法(来自聪明的CGX同学

首先,我们先写出如下几种情况:

3*2的面积中所有可能的摆放方式

那么,在3*n (n为偶数并且 n > 2)的面积中,除去上面3种方式的组合,摆放方式如下

或者把最上面那一行移到上面去(这儿省略不画)

如果除去第一种情况的互相组合,我们只可以这样摆放。

那么,根据这两种情况,我们可以得到一个递推式:

F(n)= 3 * F(n-2)+ 2 * F(n-4)+ 2 * F(n-6)+ ... +2 * F(2)

代换变量可以得到

F(n-2)= 3 * F(n-4)+ 2 * F(n-6)+ 2 * F(n-8)+ ... + 2 * F(2)

则对一式变形可得:

F(n)= 4 * F(n-2)- F(n-2)+ 2 * F(n-4)+ 2 * F(n-6)+ ... +2 * F(2)

带入二式得:

F(n)= 4 * F(n-2)- (3 * F(n-4)+ 2 * F(n-6)+ 2 * F(n-8)+ ... + 2 * F(2))+ 2 * F(n-4)+ 2 * F(n-6)+ ... +2 * F(2)

即:

F(n)= 4 * F(n-2)-  F(n-4)

这样这道题就解决啦0.0

题目给的10s所以不会超时   当然啦,用矩阵快速幂优化这个递推式,就会超级快啦0.0

代码:(愉快的代码时间)

矩阵快速幂:

#include<algorithm>
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long ll;
const int MAXN=8;
const ll MOD=12357;

struct Matrix{
    ll a[MAXN][MAXN];
    void init(){
        memset(a,0,sizeof(a));
        for(int i=0;i<MAXN;i++)
            a[i][i]=1;
    }
};

Matrix mul(Matrix a, Matrix b)
{
    Matrix ans;
    for(int i=0;i<MAXN;i++)
    {
        for(int j=0;j<MAXN;j++)
        {
            ans.a[i][j]=0;
            for(int k=0;k<MAXN;k++)
                ans.a[i][j]=(ans.a[i][j]+(a.a[i][k]*b.a[k][j])%MOD)%MOD;
        }
    }
    return ans;
}

Matrix qpow(Matrix a, ll n)
{
    Matrix ans;
    ans.init();
    while(n)
    {
        if(n&1)
            ans = mul(ans, a);
        a=mul(a,a);
        n>>=1;
    }
    return ans;
}

int main()
{
    ll n;
    while(scanf("%lld",&n)!=EOF)
    {
        Matrix a;
        memset(a.a,0,sizeof(a.a)); 
        a.a[0][7]=1;a.a[1][6]=1;a.a[2][5]=1;a.a[3][4]=1;a.a[4][3]=1;a.a[5][2]=1;a.a[6][1]=1;a.a[7][0]=1;
        a.a[3][7]=1;a.a[6][7]=1;a.a[7][3]=1;a.a[7][6]=1;

        Matrix ans=qpow(a,n);
        printf("%lld\n",ans.a[7][7]);
    }
    return 0;
}

递推:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

const int MAXN=(int)1e8+10;
const int MOD=12357;

int main()
{
	int n;
	scanf("%d",&n);
	if(n&1)
		printf("0\n");
	else if(n==0)
		printf("1\n");
	else if(n==2)
		printf("3\n");
	else
	{
		n/=2;
		long long a=1,b=3,t;
		for(int i=2;i<=n;i++)
		{
			t=((4*b-a)%MOD+MOD)%MOD;
			a=b;b=t;
		}
		printf("%lld\n",t);
	}
	return 0;
}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值