矩阵的秩,行列式,代数余子式啥的

本文介绍了矩阵的代数余子式及其计算方法,包括rank(A)为n-2、n和n-1的情况。当rank(A)=n时,提供了A*与A的行列式的乘积等于A的逆的性质。通过高斯消元法可求矩阵的秩,矩阵的逆可通过特定变换得到。最后,提到在实际问题中,如[Bzoj 3168],求逆矩阵后解决二分图最小字典序的完美匹配问题,时间复杂度为O(n^3)。

参考资料:

nealchen
Rose_max

求解A的代数余子式:

(1)rank(A)<=n-2,每个位置均为0
(2)rank(A)=n,有 A ∗ = ∣ A ∣ ∗ A − 1 A^*=|A|*A^{-1} A=AA1,其中A*是代数余子式矩阵的转置矩阵,实现的时候需要注意这点
(3)rank(A)=n-1,则求出非0行向量p,满足pA=0,以及非0列向量q,满足Aq=0
找出一对r,c,满足 p r ≠ 0 , q c ≠ 0 p_r\neq 0,q_c\neq 0 pr=0,qc=0,我们有 A i , j = p i q j p r q c A r , c A_{i,j}=\frac{p_iq_j}{p_rq_c}A_{r,c} Ai,j=prqcpiqjAr,c
实现的时候,为了方便可以设置 p r = 1 , q c = 1 p_r=1,q_c=1 pr=1,qc=1
至于求解一个矩阵的秩,模拟高斯消元,还剩下多少个非0的行向量就是矩阵的秩
至于求解一个矩阵的逆,由于保证了逆是良定义的,所以一定可以用:
1.一行乘上某个数
2.一行乘上某个数加到某行
2.交换两行
变成单位矩阵
注意到,上面3个变换都可以表示成矩阵,意思是你求出了个
E k . . . . E 1 A = I E_k....E_1A=I Ek....E1A=I,那么 E k . . E 1 = A − 1 E_k..E_1=A^{-1} Ek..E1=A1
上面的时间复杂度都是 O ( n 3 ) O(n^3) O(n3)

例题

求出代数余子式后随便做
复杂度: O ( n 3 + m ) O(n^3+m) O(n3+m)
正经人谁考场上写代数余子式啊

#include<bits/stdc++.h>
using namespace std;
const int Mod=1000000007;
int n,m;
#define Maxn 305
#define E 100010
struct Edge{
	int s,e,t;
}edge[E];
int A[Maxn][Maxn],invA[Maxn][Maxn],B[Maxn][Maxn],Ans[Maxn][Maxn],C[Maxn][Maxn];

namespace modular{
	int add(int a,int b){return a+b>=Mod?a+b-Mod:a+b;}
	int dec(int a,int b){return a-b<0?a-b+Mod:a-b;}
	int mul(int a,int b){return 1ll*a*b%Mod;}
	int Fast_Pow(int a,int b){
		int res=1;
		while(b){
			if(b&1)res=1ll*res*a%Mod;
			a=1ll*a*a%Mod;
			b>>=1;
		}
		return res;
	}
}using namespace modular;

int p[Maxn],q[Maxn];
void Gauss(int A[][Maxn],int *p,int &r){
	for(int i=1,t=1;i<=n&&t<=n;++i,++t){
		for(int j=i;j<=n;++j)
			if(A[j][t]){
				if(j^i)
					for(int k=t;k<=n;++k)swap(A[i][k],A[j][k]);
				break;
			}
		if(!A[i][t]){
			--i;r=t;
		}else{
			int f=Fast_Pow(A[i][t],Mod-2);
			for(int j=i+1;j<=n;++j){
				int v=mul(f,A[j][t]);
				if(v)
					for(int k=t;k<=n;++k)A[j][k]=dec(A[j][k],mul(A[i][k],v));
			}
		}
	}
	p[r]=1;int at;
	for(int i=n-1;i>=1;--i){
		if(i<r)at=i;
		else at=i+1;
		p[at]=0;
		for(int j=at+1;j<=n;++j)p[at]=dec(p[at],mul(p[j],A[i][j]));
		p[at]=mul(p[at],Fast_Pow(A[i][at],Mod-2));
	}
}
int Det(int A[][Maxn]){
	int res=1;
	for(int i=1;i<=n;++i){
		for(int j=i;j<=n;++j)
			if(A[j][i]){
				if(j^i){
					for(int k=i;k<=n;++k)swap(A[i][k],A[j][k]);
					res=dec(0,res);
				}
				break;
			}
			int f=Fast_Pow(A[i][i],Mod-2);
			for(int j=i+1;j<=n;++j){
				int v=mul(f,A[j][i]);
				for(int k=i;k<=n;++k)A[j][k]=dec(A[j][k],mul(A[i][k],v));
			}
		}
	for(int i=1;i<=n;++i)res=mul(res,A[i][i]);
	return res;
}
void solve(){
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j){
			A[i][j]=B[i][j];
			invA[i][j]=0;
			if(i==j)invA[i][j]=1;
		}
	int cnt=0,ty=1;
	for(int i=1,t=1;i<=n&&t<=n;++i,++t){
		for(int j=i;j<=n;++j)
			if(A[j][t]){
				if(j^i){
					for(int k=t;k<=n;++k){
						swap(A[i][k],A[j][k]);
						swap(invA[i][k],invA[j][k]);
					}
					for(int k=1;k<t;++k)swap(invA[i][k],invA[j][k]);
					ty=dec(0,ty);
				}
				break;
			}
		if(!A[i][t]){
			--i;cnt++;
		}else{
			int f=Fast_Pow(A[i][t],Mod-2);
			for(int j=i+1;j<=n;++j){
				int v=mul(f,A[j][t]);
				for(int k=t;k<=n;++k){
					A[j][k]=dec(A[j][k],mul(A[i][k],v));
					invA[j][k]=dec(invA[j][k],mul(invA[i][k],v)); 
				}
				for(int k=1;k<t;++k)invA[j][k]=dec(invA[j][k],mul(invA[i][k],v));
			}
		}
	}
	if(cnt>=2){
		for(int i=1;i<=n;++i)
			for(int j=1;j<=n;++j)Ans[i][j]=0;
		return;
	}
	if(!cnt){
		for(int i=n;i>=1;--i){
			ty=mul(ty,A[i][i]);
			for(int j=i+1;j<=n;++j)
				for(int k=1;k<=n;++k)invA[i][k]=dec(invA[i][k],mul(A[i][j],invA[j][k]));
			int t=Fast_Pow(A[i][i],Mod-2);
			for(int k=1;k<=n;++k)invA[i][k]=mul(invA[i][k],t);
		}
		for(int i=1;i<=n;++i)
			for(int j=1;j<=n;++j){
				Ans[j][i]=mul(invA[i][j],ty);
			}
		return;
	}

	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j)C[j][i]=B[i][j];
	int r,c;
	Gauss(C,p,r);
	Gauss(A,q,c);
	int t1,t2;
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j)
		if(i!=r&&j!=c){
			if(i<r)t1=i;
			else t1=i-1;
			if(j<c)t2=j;
			else t2=j-1;
			C[t1][t2]=B[i][j];
		}
	n--;
	int res=Det(C);
	if((r+c)&1)res=dec(0,res);
	n++;
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j)Ans[i][j]=mul(p[i],mul(q[j],res));
}

inline void rd(int &x){
	x=0;char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
}

int main(){
	rd(n);rd(m);
	for(int i=1;i<=m;++i){
		rd(edge[i].s);rd(edge[i].e);rd(edge[i].t);
		B[edge[i].s][edge[i].e]=dec(B[edge[i].s][edge[i].e],1);
		B[edge[i].s][edge[i].s]++;
	}
	n--;
	solve();
	n++;
	int res=0;
	for(int i=1;i<=m;++i)
		if(edge[i].s<n)
			res=add(res,mul(dec(Ans[edge[i].s][edge[i].s],Ans[edge[i].s][edge[i].e]),edge[i].t));	
	printf("%d\n",res);
	return 0;
}

[Bzoj 3168]求出逆矩阵后,变成二分图最小字典序的完美匹配
时间复杂度 O ( n 3 ) O(n^3) O(n3)

#include<bits/stdc++.h>
using namespace std;
const double eps=1e-8;
int n;
#define Maxn 305
double A[Maxn][Maxn],invA[Maxn][Maxn],B[Maxn][Maxn],C[Maxn][Maxn];

int head[Maxn<<1],v[200010],nxt[200010],tot=0;
inline void add_edge(int s,int e){tot++;v[tot]=e;nxt[tot]=head[s];head[s]=tot;}

int S,mn,fr[Maxn<<1];
int match[Maxn<<1];
bool vis[Maxn<<1];
bool dfs1(int u){
	for(int i=head[u];i;i=nxt[i])
		if(!vis[v[i]]){
			vis[v[i]]=true;
			if(!match[v[i]]||dfs1(match[v[i]])){
				match[u]=v[i];
				match[v[i]]=u;
				return true;
			}
		}
	return false;
}

void dfs2(int u){
	for(int i=head[u];i;i=nxt[i])
		if(!vis[v[i]]){
			vis[v[i]]=true;
			fr[match[v[i]]]=u;
			dfs2(match[v[i]]);
		}else if(v[i]==S)mn=min(mn,u);
}

void calc_inv(){
	for(int i=1;i<=n;++i)invA[i][i]=1.0;
	for(int i=1;i<=n;++i){
		if(fabs(A[i][i])<eps){
			for(int j=i+1;j<=n;++j)
				if(fabs(A[j][i])>=eps){
					for(int k=i;k<=n;++k){
						swap(A[i][k],A[j][k]);
						swap(invA[i][k],invA[j][k]);
					}
					for(int k=1;k<i;++k)swap(invA[i][k],invA[j][k]);
					break;
				}
		}
		if(fabs(A[i][i])<eps){
			puts("NIE");
			exit(0);
		} 
		for(int j=i+1;j<=n;++j){
			double t=A[j][i]/A[i][i];
			for(int k=i;k<=n;++k)A[j][k]-=A[i][k]*t,invA[j][k]-=invA[i][k]*t;
			for(int k=1;k<i;++k)invA[j][k]-=invA[i][k]*t;
		}
	}
	for(int i=n;i>=1;--i){
		for(int j=i+1;j<=n;++j)
			for(int k=1;k<=n;++k)invA[i][k]=(invA[i][k]-invA[j][k]*A[i][j]);
		for(int j=1;j<=n;++j)invA[i][j]=invA[i][j]/A[i][i];
	}
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j)scanf("%lf",&A[i][j]);
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j)scanf("%lf",&B[i][j]);
	calc_inv();
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j)
			for(int k=1;k<=n;++k)C[i][k]+=B[i][j]*invA[j][k];
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j){
			if(fabs(C[i][j])>=eps)add_edge(j,i+n),add_edge(i+n,j);
		}
	for(int i=1;i<=n;++i)
	if(!match[i]){
		memset(vis,false,sizeof(bool)*(2*n+1));
		if(!dfs1(i)){
			puts("NIE");
			return 0;
		}
	}
	puts("TAK");
	for(int i=1;i<=n;++i){
		S=i;mn=match[i];
		for(int j=i+1;j<=n;++j)vis[j]=false;
		vis[i]=true;
		dfs2(match[i]);int z=match[i];
		int at=mn,pre=match[at];
		while(at!=z){
			int to=pre;
			match[to]=fr[at];
			pre=match[fr[at]];
			match[fr[at]]=to;
			at=fr[at];
		}
		match[i]=mn;match[mn]=i;
	}
	for(int i=1;i<=n;++i)printf("%d\n",match[i]-n);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值