矩阵专题

参考矩阵资料:资料

首先是一道入门题

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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值