ACM竞赛学习整理–矩阵运算
了解矩阵类
【任务】
实现矩阵的基本变换
【接口】
结构体:Matrix
成员变量:
int n,m 矩阵大小
int a[][] 矩阵内容
重载运算符: +、-、x
成员函数:void clear() 清空矩阵
【代码】
const int MAXN = 1010;
const int MAXM = 1010;
struct Matrix{
int n,m;
int a[MAXN][MAXM];
void clear(){
n=m=0;
memset(a,0,sizeof(a));
}
}
Matrix operator +(const Matrix &b) const{
Matrix tmp;
tmp.n = n;
tmp.m = m;
for(int i = 0;i<n;++i)
for(int j=0;j<m;++j)
tmp.a[i][j] = a[i][j] + b.a[i][j];
return tmp;
}
Matrix operator -(const Matrix &b) const{
Matrix tmp;
tmp.n = n;
tmp.m = m;
for(int i = 0;i<n;++i)
for(int j=0;j<m;++j)
tmp.a[i][j] = a[i][j] - b.a[i][j];
return tmp;
}
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] += a[i][k] * b.a[k][j];
return tmp;
}
【扩展】
关于矩阵加速数列递推:
给定一个递推数列
f[i]=a1∗f[i−1]+a2∗f[i−2]…ak∗f[i−k] ,
我们普通计算的话肯定是逐个计算,复杂度较大。
我们可以用矩阵表示:
为了递推出f[n] , 我们需要找到一个系数矩阵 A 使得:
就可以这样计算:
A矩阵可以这样构造:
至此,我们可以愉快地用矩阵加速递推数列了。
参考来源:https://www.cnblogs.com/alecli/p/10004417.html
【应用】
poj 2663详解
Description
In how many ways can you tile a 3xn rectangle with 2x1 dominoes?
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
分析题目,这是一个递推关系求解题,并且很容易发现当n为奇数时,无法找到满足题意的铺法,以下仅考虑n为偶数的情况。
思路:
这里先定义两个概念:独立单位和非独立单位。独立单位即不可纵向切割的矩形块, 非独立单位即可纵向切割的矩形块。
我们先来试试几种简单情况。当n = 2时,有3种铺法,如下图。
当n=4时。组合分为两种情况:一种是两个n=2的独立单位组合,另一种是一个n=0的独立单位组合上一个n=4的独立单位。分别如下图。
两个n=2的独立单位组合,比较简单,铺法为3x3。一个n=0的独立单位组合上一个n=4的独立单位,通过画图可以判断,只有两种情况,分别为:
那么n=4的总铺法数就容易确定了,为 3xa[2]+2xa[0] (这里a[0]=1,至于为什么等于1,请读者自己体会)
接下来考虑n= 6,这时有三种组合情况,分别为:
- 一个n=4的独立单位连接上一个n=2的独立单位;
- 一个n=2的独立单位连接上一个n=4的独立单位;
- 一个n=0的独立单位连接上一个n=6的独立单位。
组合1:铺法为a[4]x3;
组合2:铺法为a[2]x2;
组合3:铺法为a[0]x2;
故n=6时的总铺法数为:a[4]x3+a[2]x2+a[0]x2。
(有人可能会问为什么要乘以2,这里请读者自行画图求解…)
当n更大时,以此类推。
所以最后我们得出a[n] = 3xa[n-2]+2x(a[n-4]+a[n-6] +…+a[0]),再运用高中所学数列知识,化简得:a[n] = 4a[n-2] - a[n-4];
代码整理如下:
#include<iostream>
#include<string.h>
using namespace std;
const int MAXN=10;
const int MAXM=10;
const long long mod = 1000007;
int r;
//矩阵类模板
struct Matrix{
int n,m;
int a[MAXN][MAXM];
void clear(){
n=m=0;
memset(a,0,sizeof(a));
}
Matrix operator +(const Matrix &b) const {
Matrix tmp;
tmp.n=n;tmp.m=m;
for (int i=0;i<n;++i)
for(int j=0;j<m;++j)
tmp.a[i][j]=a[i][j]+b.a[i][j];
return tmp;
}
Matrix operator -(const Matrix &b)const{
Matrix tmp;
tmp.n=n;tmp.m=m;
for (int i=0;i<n;++i)
for(int j=0;j<m;++j)
tmp.a[i][j]=a[i][j]-b.a[i][j];
return tmp;
}
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]+=a[i][k]*b.a[k][j];
tmp.a[i][j]%=mod;
}
return tmp;
}
Matrix get(int x){//幂运算
Matrix E;
E.clear();
E.n=E.m=n;
for(int i=0;i<n;++i)
E.a[i][i]=1;
if(x==0) return E;
else if(x==1) return *this;
Matrix tmp=get(x/2);
tmp=tmp*tmp;
if(x%2) tmp=tmp*(*this);
return tmp;
}
};
//矩阵模板结束
int main(){
while(cin>>r){
if(r==0) break;
int a[]= {1,0,3,0};
if(r<=3)
{
cout<<a[r]%mod<<endl;
continue;
}
Matrix A;
A.clear();
A.n=A.m=4;
A.a[0][1]=A.a[1][2]=A.a[2][3]=1;
A.a[3][0]=-1;
//A.a[3][1]=1;
A.a[3][2]=4;
//A.a[3][3]=1;
A=A.get(r-3);
Matrix M;
M.clear();
M.n=4;M.m=1;
M.a[0][0]=1;
M.a[2][0]=3;
M.a[3][0]=0;
M=A*M;
cout<<(M.a[3][0]+mod)%mod<<endl;
}
return 0;
}
ps: ACM国际大学生程序设计与竞赛-算法基础(俞勇主编)里有介绍矩阵的实现,刚好手上有个项目要用到矩阵的操作, 一开始想用Eigen 库来实现矩阵的操作,后来想想干脆还是C去实现吧。下午空闲一会了,找了一个ACM的例题来做做。于是就拙了一篇,请大家多指正。