hiho42 : 骨牌覆盖问题·二
描述
上一周我们研究了2xN的骨牌问题,这一周我们不妨加大一下难度,研究一下3xN的骨牌问题?
所以我们的题目是:对于3xN的棋盘,使用1x2的骨牌去覆盖一共有多少种不同的覆盖方法呢?
首先我们可以肯定,奇数长度一定是没有办法覆盖的;对于偶数长度,比如2,4,我们有下面几种覆盖方式:
输入
第1行:1个整数N。表示棋盘长度。1≤N≤100,000,000
输出
第1行:1个整数,表示覆盖方案数 MOD 12357
样例输入
62247088
样例输出
4037
提示:3xN骨牌覆盖
在2xN的骨牌覆盖问题中,我们有递推式子 (0,1)xM^n=(f[n-1],f[n])。
我们考虑能否在3xN的情况下找到同样的式子。
但在实际的推导过程可以发现,对于3xN的覆盖,对应的f数值公式比2xN复杂太多。我们需要换个角度来思考推导公式。
在我们放置骨牌的过程中,一定是放好一行之后再放置下一行。根据摆放的方式,可能会产生很多种不同的形状,而这些形状之间是否具有某些递推关系呢?
如果他们存在一定的递推关系,则我们可以根据第i行的方案数来推导第i+1行的方案数。这样一行一行推导,直到第N行时不就得到了我们要求的方案数了么?
那么来研究一下是否存在这样的推导公式吧
假设我们已经放好了一些骨牌,对于当前最后一列(第i列)骨牌,可能有8种情况:
对于上面这8种状态,我们用数字来标记它们。以有放置骨牌的格子为1,未放置为0,转化为2进制数
以最下面一行作为1,则有:
接下来考虑如何放置骨牌,我们先将棋盘旋转一下。假设我们正在放置第i行的骨牌,那么会有下面3种方式:
灰色表示已经有的骨牌,绿色表示新放置的骨牌。
每一种放置方法解释如下,假设当第i行的状态为x,第i-1行的状态为y:
- 第i行不放置,则前一行必须有放置的骨牌。x对应二进制位为0,y对应二进制位为1。
- 第i行竖放骨牌,则前一行必须为空。x对应二进制位为1,y对应二进制位为0。
- 第i行横向骨牌,则前一行必须两个位置均有骨牌,否则会产生空位。x对应二进制位为1,y对应二进制位为1。
举个例子:
对于第i行状态1,我们在第i+1行竖放两块骨牌之后便能到达状态6。
但是在这之中需要注意会出现下面这种情况:
这种情况看似是从状态1变成了状态0,其实是不对的。它不满足我们约定的放置方法,本质是第i行的状态1变成了第i行的状态7,而实际上我们应该放置的是第i+1行。
所以在枚举递推关系的时候一定要注意。
通过枚举8种状态到8种状态的转移,我们可以得到一个8x8的矩阵M(空白的地方均为0):
m[i][j]表示从状态i变成状态j的方案数。
现在我们有了M矩阵,接下来考虑边界情况。
在2xN的骨牌覆盖中,有(0, 1)作为初始向量A,那么在3xN中初始向量A是如何呢?
让我们先想想A向量所代表的含义。M矩阵表示状态到状态的转移,则A向量所表示的应该就是第0行各状态的方案数。
同理,对于A * M^n所求出的结果则应该表示为第n行各种状态的方案数。
那么A向量应该是多少呢?很显然,第0行在我们递推的过程中必须看作状态7才合理。故A向量表示为:
{0, 0, 0, 0, 0, 0, 0, 1}
而对于我们寻求的答案,自然也是第n行放置为状态7的方案数了。
#include <iostream> using namespace std; typedef long long ll; const int M = 12357;
struct Matrix
{
int m[8][8];
Matrix()
{ for(int i=0; i<8; i++)
{
for (int j=0; j<8; j++)
{
m[i][j]=0;
}
}
for(int i=0; i<8; i++)
{
m[i][7-i]=1;
}
m[3][7]=1;
m[7][3]=1;
m[6][7]=1;
m[7][6]=1;
}
Matrix operator*(Matrix& a)
{
Matrix res; for (int i=0; i<8; i++)
{
for (int j=0; j<8; j++)
{
res.m[i][j]=0;
for (int k=0; k<8; k++)
{
res.m[i][j]=(res.m[i][j] + (ll)m[i][k]*a.m[k][j])%M;
}
}
}
return res;
}
}; Matrix pow(Matrix m, int n)
{
Matrix res;
if(1==n)
return m;
res = pow(m, n/2);
if(n%2==1)
res = res*res*m;
else
res = res*res;
return res;
} int main()
{
int N;
cin>>N; Matrix mat; mat = pow(mat, N); cout<<mat.m[7][7]; return 0;
}
hiho42 : 骨牌覆盖问题·二的更多相关文章
- hiho #1151 : 骨牌覆盖问题·二 (递推,数论)
#1151 : 骨牌覆盖问题·二 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 上一周我们研究了2xN的骨牌问题,这一周我们不妨加大一下难度,研究一下3xN的骨牌问题? ...
- hihoCode #1151 : 骨牌覆盖问题·二
#1151 : 骨牌覆盖问题·二 Time Limit:10000ms Case Time Limit:1000ms Memory Limit:256MB 描述 上一周我们研究了2xN的骨牌问题,这一 ...
- [hihoCoder] 骨牌覆盖问题·二
时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 上一周我们研究了2xN的骨牌问题,这一周我们不妨加大一下难度,研究一下3xN的骨牌问题?所以我们的题目是:对于3xN的棋盘 ...
- hihoCoder #1151 : 骨牌覆盖问题·二 (矩阵快速幂,DP)
题意:给一个3*n的矩阵,要求用1*2的骨牌来填满,有多少种方案? 思路: 官网题解用的仍然是矩阵快速幂的方式.复杂度O(logn*83). 这样做需要构造一个23*23的矩阵,这个矩阵自乘n-1次, ...
- 骨牌覆盖问题总结!hihoCoder/ NYOJ-1273宣传墙1151
本想着做一下第九届河南省省赛题,结果被这个类似骨牌覆盖的题卡住了,队友然我去hihoCoder上老老实实把骨牌覆盖一.二.三做完,这题就没什么问题了.虽然很不情愿,但还是去见识了一下. 骨牌覆盖问题 ...
- hihocoder第42周 3*N骨牌覆盖(状态dp+矩阵快速幂)
http://hihocoder.com/contest/hiho42/problem/1 给定一个n,问我们3*n的矩阵有多少种覆盖的方法 第41周做的骨牌覆盖是2*n的,状态转移方程是dp[i] ...
- 随便玩玩系列之一:SPOJ-RNG+51nod 算法马拉松17F+51nod 1034 骨牌覆盖v3
先说说前面的SPOJ-RNG吧,题意就是给n个数,x1,x2,...,xn 每次可以生成[-x1,x1]范围的浮点数,把n次这种操作生成的数之和加起来,为s,求s在[A,B]内的概率 连续形的概率 假 ...
- hiho #1143 : 骨牌覆盖问题·一 (运用快速幂矩阵)
#1143 : 骨牌覆盖问题·一 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 骨牌,一种古老的玩具.今天我们要研究的是骨牌的覆盖问题:我们有一个2xN的长条形棋盘,然 ...
- hiho41 : 骨牌覆盖问题·一
原问题:骨牌覆盖问题 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 骨牌,一种古老的玩具.今天我们要研究的是骨牌的覆盖问题:我们有一个2xN的长条形棋盘,然后用1x2的 ...
随机推荐
- GDUT 校赛01 dp
aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAABT8AAAILCAIAAAChHn9YAAAgAElEQVR4nOy9f4il13nneUGgxrRYux ...
- ava中Class.forName的作用浅谈
转自:http://www.jb51.net/article/42648.htm Class.forName(xxx.xx.xx) 返回的是一个类 一.首先你要明白在java里面任何class都要装载 ...
- matlab练习程序(透视投影,把lena贴到billboard上)
本练习程序是受到了这个老外博文的启发,感觉挺有意思,就尝试了一下.他用的是opencv,我这里用的是matlab. 过去写过透视投影,当时是用来做倾斜校正的,这次同样用到了透视投影,不过更有意思,是将 ...
- 【JUnit 报错】java.lang.IncompatibleClassChangeError
使用Junit 测试spring时候报错: java.lang.IncompatibleClassChangeError: class org.springframework.core.LocalVa ...
- Quartz.net misfire实践
1.问题描述 在使用Quartz.net定时运行作业时,存在一种情况:作业错过了某次执行,当作业恢复的时候应该怎么处理?如:job1在3:50的时候应该执行的,但此刻job1处于暂停状态,而到3: ...
- COJ990 WZJ的数据结构(负十)
DFS序(带入栈出栈标记): 对于一个节点,我们用L[i]和R[i]表示它入栈和出栈的时间.这样[L[i],R[i]]就表示了以i为根的区间. 我们还要将入栈的符号为+,出栈的符号为-,那么令V[i] ...
- hilbert
hilbert 难度级别: A: 编程语言:不限:运行时间限制:1000ms: 运行空间限制:131072KB: 代码长度限制:102400B 试题描述 图1为1阶Hilbert曲线,它由3条长度为1 ...
- javascript第四弹——变量、作用域、内存
一.变量 定义 变量是松散型的 变量是保存特定值的一个名字 变量包含两种数据类型的值:基本数据类型的值和引用数据类型的值 基本数据类型值 基本数据类型值是一个简单的数据段,在内存中占用固定的空间,保存 ...
- java定时器的使用
定时器类Timer在java.util包中.使用时,先实例化,然后使用实例的schedule(TimerTask task, long delay)方法,设定指定的任务task在指定的延迟delay后 ...
- Sublime Text 2的快速入门和常用技巧
1. 安装扩展器包管理器Package Control组件 点击菜单 View -> Show Console 调出控制台或者按快捷键 “Ctrl + `”(1左边的符号,可能和QQ拼音输入法和 ...