参考矩阵资料:资料
首先是一道入门题
NYOJ623
就是求两个矩阵的乘积, 数据又给的很友好所以
#include<stdio.h>
#include<cstring>
typedef long long ll;
const int qq = 55;
struct rec{
ll mar[qq][qq];
rec(){
memset(mar, 0, sizeof(mar));
}
}a, b;
void multi(int m, int n, int k){
rec tmp;
for(int i=0; i<m; ++i)
for(int j=0; j<k; ++j)
for(int l=0; l<n; ++l)
tmp.mar[i][j]+=a.mar[i][l]*b.mar[l][j];
for(int j,i=0; i<m; ++i){
for(j=0; j<k-1; ++j)
printf("%lld ", tmp.mar[i][j]);
printf("%lld\n", tmp.mar[i][j]);
}
}
int main(){
int n,m,k;
while(scanf("%d%d%d", &m, &n, &k)!=EOF){
if(n+m+k<=0) break;
for(int i=0; i<m; ++i)
for(int j=0; j<n; ++j) scanf("%lld", &a.mar[i][j]);
for(int i=0; i<n; ++i)
for(int j=0; j<k; ++j) scanf("%lld", &b.mar[i][j]);
multi(m, n, k);
}
return 0;
}
然后是简单的矩阵快速幂
NYOJ 148
求fibonacc数列的第n项,此时的n高达1e9、 此时用矩阵优化, 线性递推的题目很多都可用矩阵做
#include<cmath>
#include<cstring>
#include<cstdio>
using namespace std;
const int mod = 1e4;
struct rec{
int mar[2][2];
rec operator * (const rec &a)const{
rec tmp;
memset(tmp.mar, 0, sizeof(tmp.mar));
for(int i=0; i<2; ++i)
for(int j=0; j<2; ++j)
for(int k=0; k<2; ++k){
tmp.mar[i][j]+=mar[i][k]*a.mar[k][j];
tmp.mar[i][j]%=mod;
}
return tmp;
}
};
void quick(int n){
rec ans;
memset(ans.mar, 0, sizeof(ans.mar));
ans.mar[0][0] = ans.mar[1][1] = 1;
rec tmp;
tmp.mar[1][0] = tmp.mar[0][1] = tmp.mar[0][0] = 1;
tmp.mar[1][1] = 0;
while(n){
if(n&1) ans = ans * tmp;
tmp = tmp*tmp;
n>>=1;
}
printf("%d\n", ans.mar[0][1]);
}
int main(){
int n;
while(scanf("%d", &n)!=EOF){
if(n==-1) break;
quick(n);
}
return 0;
}
POJ 3233
求S = A + A2 + A3 + … +Ak.
A是一个矩阵, k是一个整数
此时有两种方法, 第一种是构造矩阵, 第二种是直接二分
此处的代码是直接二分,当然构造矩阵要快一点
k为偶数时 S (k)= (E + A^(k/2) ) * S(k/2)
k为奇数时 S (k)= (E + A^(k/2) ) * S(k/2) + A;
可以参考:传送门
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<string>
#include<iostream>
using namespace std;
const int qq = 32;
int m,n,k;
struct rec{
int mar[qq][qq];
rec(){
memset(mar, 0, sizeof(mar));
}
rec operator * (const rec &a)const{
rec tmp;
for(int i=0; i<n; ++i)
for(int j=0; j<n; ++j)
for(int k=0; k<n; ++k)
tmp.mar[i][j] = (tmp.mar[i][j] + mar[i][k]*a.mar[k][j])%m;
return tmp;
}
rec operator + (const rec &b)const{
rec tmp;
for(int j,i=0; i<n; ++i)
for(j=0; j<n; ++j)
tmp.mar[i][j] = (mar[i][j]+b.mar[i][j])%m;
return tmp;
}
};
rec mtpow(rec A, int k){ //求A^k
rec tmp;
for(int i=0; i<n; ++i)
tmp.mar[i][i] = 1;
if(k==0){
return tmp;
}
while(k){
if(k&1) tmp = tmp*A;
A = A*A;
k>>=1;
}
return tmp;
}
rec mtcal(rec A, int k){ // 求S(k)= A + A2 + A3 + … + Ak.
if(k==1) return A;
rec B = mtpow(A, (k+1)/2);
rec C = mtcal(A, k/2);
if(k%2==1){ // 求 S(k) = A (A + A^(k/2))*S(k/2)
return A + (A+B)*C ;
}else{
return (mtpow(A, 0)+B)*C; // 求S = (E + A^(k/2))*S(k/2)
}
}
int main(){
scanf("%d%d%d", &n, &k, &m);
rec ans;
for(int j,i=0; i<n; ++i)
for(j=0; j<n; ++j) scanf("%d", &ans.mar[i][j]);
ans = mtcal(ans, k);
for(int j,i=0; i<n; ++i){
for(j=0; j<n-1; ++j)
printf("%d ", (ans.mar[i][j]+m)%m);
printf("%d\n", (ans.mar[i][j]+m)%m);
}
return 0;
}
然后就是求从i走到j恰好走k步的
HDU 2157
这题有个坑点 就是从某个点到某个点最多只有一条路、
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<string>
#include<queue>
using namespace std;
typedef long long ll;
const int qq = 22;
const int mod = 1000;
int n,m;
struct rec{
int mar[qq][qq];
rec(){
memset(mar, 0, sizeof(mar));
}
rec operator * (const rec &a)const{
rec tmp;
for(int j,i=0; i<n; ++i)
for(j=0; j<n; ++j)
for(int k=0; k<n; ++k)
tmp.mar[i][j]=(tmp.mar[i][j]+mar[i][k]*a.mar[k][j])%mod;
return tmp;
}
};
rec quick_pow(rec A, int a){
rec tmp;
for(int i=0; i<n; ++i) tmp.mar[i][i] = 1;
while(a){
if(a&1) tmp = tmp*A;
A = A*A;
a>>=1;
}
return tmp;
}
int main(){
while(scanf("%d%d", &n,&m)!=EOF){
if(n==0&&m==0) break;
rec ans;
for(int i=0; i<m; ++i){
int a,b;scanf("%d%d", &a, &b);
ans.mar[a][b]=1; //改成++就错了
}
int k;scanf("%d", &k);
for(int i=0; i<k; ++i){
int a,b,c;scanf("%d%d%d", &a, &b, &c);
rec res = quick_pow(ans, c);
printf("%d\n", (res.mar[a][b]+mod)%mod);
}
}
return 0;
}
然后就是关于转置、
HDU 2371
题意:就说有一个字符串, 然后给你转置的序列, 这个字符串经过转置m次以后得到了另外一个字符串, 现在给你这个另外的字符串, 要你求元字符串
思路:利用矩阵转置, 首先你得到的是转置后的序列, 我们无非就是把它再转回去, 根据转置序列我们构造一个反转置序列,然后直接快速幂就行了、
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
using namespace std;
const int qq = 82;
int n,k;
struct rec{
int mar[qq][qq];
rec(){
memset(mar, 0, sizeof(mar));
}
rec operator * (const rec &a)const{
rec tmp;
for(int j,i=0; i<n; ++i)
for(j=0; j<n; ++j)
for(int k=0; k<n; ++k)
tmp.mar[i][j]+=(mar[i][k]*a.mar[k][j]);
return tmp;
}
};
char str[qq];
int num[qq];
int tnum[qq];
rec quick_pow(rec A, int b){
rec tmp;
for(int i=0; i<n; ++i) tmp.mar[i][i] = 1;
while(b){
if(b&1) tmp = tmp*A;
A = A*A;
b>>=1;
}
return tmp;
}
int main(){
while(scanf("%d%d", &n, &k)!=EOF){
if(n+k<=0) break;
for(int i=0; i<n; ++i)
scanf("%d", &num[i]);
for(int i=0; i<n; ++i) //构造反转置序列、
tnum[num[i]-1] = i;
rec ans;
for(int i=0; i<n; ++i)
ans.mar[i][tnum[i]] = 1;
getchar();
gets(str);
int x;
ans = quick_pow(ans, k);
for(int i=0; i<n; ++i){
for(int j=0; j<n; ++j)
if(ans.mar[i][j]!=0) x = j;
printf("%c", str[x]);
}
puts("");
}
return 0;
}
线性递推题、
HDU 2601
题意:就说有n个人排队,只有男生和女生, 那么就有2^n的排法, 问不包含fff fmf的排法
思路:碰到这种题我首先想到了打表 - - 奈何规律好像 , 就是找不出来, 后面具体分析一下还是很清楚
参考:传送门
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<string>
using namespace std;
const int qq = 6;
int n,m;
struct rec{
int mar[qq][qq];
rec(){
memset(mar, 0, sizeof(mar));
}
rec operator * (const rec &a)const{
rec tmp;
for(int k,j,i=0; i<4; ++i)
for(j=0; j<4; ++j)
for(k=0; k<4; ++k)
tmp.mar[i][j] = (tmp.mar[i][j]+mar[i][k]*a.mar[k][j])%m;
return tmp;
}
};
rec quick_pow(rec x, int k){
rec tmp;
for(int i=0; i<4; ++i)
tmp.mar[i][i] = 1;
while(k){
if(k&1) tmp = tmp*x;
x = x*x;
k>>=1;
}
return tmp;
}
int num[10] = {9, 6, 4, 2};
int main(){
while(scanf("%d%d", &n, &m)!=EOF){
if(n<=4){
printf("%d\n", (num[(4-n)])%m);
continue;
}
rec ans;
ans.mar[0][0] = ans.mar[0][2] = ans.mar[0][3] = 1;
for(int i=1; i<4; ++i)
ans.mar[i][i-1] = 1;
ans = quick_pow(ans, n-4);
// for(int j,i=0; i<4; ++i){
// for(j=0; j<4; ++j)
// printf("%d ", ans.mar[i][j]);
// printf("\n");
// }
int sum = 0;
for(int i=0; i<4; ++i)
sum = (sum + ans.mar[0][i]*num[i])%m;
printf("%d\n", (sum+m)%m);
}
return 0;
}