题目大意
4*N的方格,你用 2 ∗ 1 2*1 2∗1的矩形来填充,有多少种填充方式。
思路分析
从简到难依次进行分析
记
f
(
k
)
f(k)
f(k)为
n
=
k
n=k
n=k时的排列总数,
f
(
0
)
=
1
f(0)=1
f(0)=1
-
n = 1 n=1 n=1的时候,只有一种排列,再乘以一行之前的排列数,即 f ( 1 ) = 1 ∗ f ( 0 ) f(1)=1*f(0) f(1)=1∗f(0)
-
n = 2 n=2 n=2的时候,首先考虑第二列是上图的情况,有 f ( 2 ) = 1 ∗ f ( 1 ) f(2)=1*f(1) f(2)=1∗f(1),再考虑两行并在一起的情况,有四种情况,也就是说 f ( 2 ) = 1 ∗ f ( 1 ) + 4 ∗ f ( 0 ) = 5 f(2)=1*f(1)+4*f(0)=5 f(2)=1∗f(1)+4∗f(0)=5
-
n = 3 n=3 n=3的时候,最后一列单独拿出来有1种情况 1 ∗ f ( 2 ) 1*f(2) 1∗f(2),最后两列单独拿出来有4种情况, 4 ∗ f ( 1 ) 4*f(1) 4∗f(1),那么三列单独拿出来呢?有两种,也就是说 f ( 3 ) = 1 ∗ f ( 2 ) + 4 ∗ f ( 1 ) + 2 ∗ f ( 0 ) = 11 f(3)=1*f(2)+4*f(1)+2*f(0)=11 f(3)=1∗f(2)+4∗f(1)+2∗f(0)=11
注意右图四个不算在三列单独拿出来的种类中。对于第三个图而言,他的最后一列依然可以单独拿出来。
-
n = 4 n=4 n=4的时候,仍然具有四列的不可拆分的组合,你不能把下图拆成任意的1-3的组合的组合。所以 f ( 4 ) = 1 ∗ f ( 3 ) + 4 ∗ f ( 2 ) + 2 ∗ f ( 1 ) + 3 ∗ f ( 0 ) = 36 f(4)=1*f(3)+4*f(2)+2*f(1)+3*f(0)=36 f(4)=1∗f(3)+4∗f(2)+2∗f(1)+3∗f(0)=36
-
n = 5 n=5 n=5,将5列单独拿出来,又有两种情况
-
n > 4 n> 4 n>4,比如5列的时候,四列的任何排列都可以分解成1-3列其中几种的组合,因此不独立。 f ( 5 ) = f ( 4 ) + 4 f ( 3 ) + 2 f ( 2 ) + 3 f ( 1 ) + 2 f ( 0 ) = 95 f(5)=f(4)+4f(3)+2f(2)+3f(1)+2f(0)=95 f(5)=f(4)+4f(3)+2f(2)+3f(1)+2f(0)=95。
到这里我们来分析一下这么些个情况,后三项呈现出2,3,2的系数,这是一个巧合吗,不妨回去看看3列不可拆分的情况,5列不可拆分的情况,你会发现5列不可拆分的情况,就是在三列的基础上进行了填补,所以5列单独拿出来和三列单独拿出来的组合数一样,同样的,如果再次对6进行推导,你会发现6是4的填充,于是我们得到如下的结论。
令
a
[
i
]
a[i]
a[i]表示单独i
列的组合数目,那么
a
[
0
]
=
a
[
1
]
=
1
,
a
[
2
]
=
4
,
a
[
2
∗
k
]
=
3
,
a
[
2
∗
k
+
1
]
=
2
a[0]=a[1]=1,a[2]=4,a[2*k]=3,a[2*k+1]=2
a[0]=a[1]=1,a[2]=4,a[2∗k]=3,a[2∗k+1]=2
于是我们就得到了递推关系式
f
(
n
)
=
∑
i
=
n
−
1
0
a
[
i
]
f
(
i
)
f(n)=\sum_{i=n-1}^{0}{a[i]f(i)}
f(n)=i=n−1∑0a[i]f(i)
此时我们几行代码就可以搞定这道题 ,如果
N
N
N在100之内,我们只需要几行代码就可以搞定这道题。
a[0] = a[1] = 1; a[2] = 4;
for (int i = 3; i < 100; i++) a[i] = (i % 2) == 0 ? 3 : 2;
f[0] = 1;
for (int i = 1; i < 105; i++) {
for (int j = i - 1; j >= 0; j--) {
f[i] += a[i - j] * f[j];
}
}
转身一看
1
≤
N
≤
1
0
9
1 ≤ N ≤ 10^9
1≤N≤109,显然我们没有这么大的数组。那么怎么办呢?不妨找一下
f
(
n
)
f(n)
f(n)与
f
(
n
−
1
)
f(n-1)
f(n−1)的关系,尽量计算
f
(
n
)
f(n)
f(n)时不需要太多的位数
f
(
n
)
=
f
(
n
−
1
)
+
4
f
(
n
−
2
)
+
2
f
(
n
−
3
)
+
3
f
(
n
−
4
)
+
.
.
.
f(n)=f(n-1)+4f(n-2)+2f(n-3)+3f(n-4)+...
f(n)=f(n−1)+4f(n−2)+2f(n−3)+3f(n−4)+...
f
(
n
−
1
)
=
f
(
n
−
2
)
+
4
f
(
n
−
3
)
+
2
f
(
n
−
4
)
+
3
f
(
n
−
5
)
+
.
.
.
f(n-1)=f(n-2)+4f(n-3)+2f(n-4)+3f(n-5)+...
f(n−1)=f(n−2)+4f(n−3)+2f(n−4)+3f(n−5)+...二者相加
f
(
n
)
=
5
f
(
n
−
2
)
+
6
f
(
n
−
3
)
+
5
(
f
(
n
−
4
)
+
f
(
n
−
5
)
+
.
.
.
)
f(n)=5f(n-2)+6f(n-3)+5(f(n-4)+f(n-5)+...)
f(n)=5f(n−2)+6f(n−3)+5(f(n−4)+f(n−5)+...)那么
f
(
n
−
1
)
=
5
f
(
n
−
3
)
+
6
f
(
n
−
4
)
+
5
(
.
.
.
)
f(n-1)=5f(n-3)+6f(n-4)+5(...)
f(n−1)=5f(n−3)+6f(n−4)+5(...)二者相减
f
(
n
)
=
f
(
n
−
1
)
+
5
f
(
n
−
2
)
+
f
(
n
−
3
)
−
f
(
n
−
4
)
f(n)=f(n-1)+5f(n-2)+f(n-3)-f(n-4)
f(n)=f(n−1)+5f(n−2)+f(n−3)−f(n−4)这样
那我们是不是可以将数组滚动一下
#include<iostream>
using namespace std;
#define MAX 10000000001,
int f[10];
int main() {
int n = 0, m = 0;
while (cin >> n >> m && n + m > 0) {
f[0] = 1; f[1] = 1; f[2] = 5; f[3] = 11; f[4] = 36;
for (int i = 5; i <= n; i++) {
int i1 = i % 10, i2 = (i - 1) % 10, i3 = (i - 2) % 10, i4 = (i - 3) % 10, i5 = (i - 4) % 10;
f[i1] = (f[i2] + (5 * f[i3]) % m + f[i4] - f[i5]) % m;
}
cout << f[n] << endl;
}
}
没有MLE!但是TLE,数据范围还是太大 ,我们无法避免每次循环都要跑一次完整的程序(因为内存不够),如何优化?再从公式入手,联想一下斐波那契数列我们构造矩阵进行运算,这里也是可以的。
f
(
n
)
=
f
(
n
−
1
)
+
5
f
(
n
−
2
)
+
f
(
n
−
3
)
−
f
(
n
−
4
)
f(n)=f(n-1)+5f(n-2)+f(n-3)-f(n-4)
f(n)=f(n−1)+5f(n−2)+f(n−3)−f(n−4)
五个变量值,我们取前四个作为目标值,后四个作为自变量,
于是目标就变成了如何构造矩阵A,经过观察我们可以得到,A应该是
于是乎我们得到了,
初始化
只需要求出
C
=
A
n
C=A^n
C=An,然后输出
C
[
0
]
∗
B
C[0]*B
C[0]∗B即可
还有一些小问题需要注意,错了一中午,在代码里标注了。(百炼AC了,结果Poj CR,他的编译器总给出不太支持数组用{}初始化,懒得改了)
#include<iostream>
using namespace std;
#define MAX 10000000001,
int n, m;
struct matrix {
int a[4][4] = { {1,5,1,-1},{1,0,0,0},{0,1,0,0} ,{0,0,1,0} };
void init1() { a[0][0] = a[1][1] = a[2][2] = a[3][3] = 1; }
void init0() { for (int i = 0; i < 4; i++)for (int j = 0; j < 4; j++)a[i][j] = 0; }
void operator = (const matrix & m) {
for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) a[i][j] = m.a[i][j];
}
};
matrix mul(matrix a, matrix b) {
matrix res; res.init0();
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
for (int k = 0; k < 4; k++)
res.a[i][j] = (res.a[i][j] + (a.a[i][k] * b.a[k][j]) % m) % m;
return res;
}
//a^p
matrix quickPow(matrix a, int p) {
matrix res; res.init0(); res.init1();//(1)对角矩阵!谨记这个初始化
while (p > 0) {
if (p & 1) res = mul(res, a);
a = mul(a, a);
p >>= 1;
}
return res;
}
int B[4][1] = { 11,5,1,1 };
int main() {
while (cin >> n >> m && n + m > 0) {
if (n <= 3) {//(2)特判,不要忘了取余
cout << B[3 - n][0] % m << endl; continue;
}
matrix tmp;
matrix A = quickPow(tmp, n - 3);//(3)注意是n-3次方
int res = 0;
for (int i = 0; i < 4; i++)res = (res + (A.a[0][i] * B[i][0]) % m) % m;
cout << (res + m) % m << endl;//(4)res值可能为负,需要进行操作
}
}