参考资料:
求解A的代数余子式:
(1)rank(A)<=n-2,每个位置均为0
(2)rank(A)=n,有
A
∗
=
∣
A
∣
∗
A
−
1
A^*=|A|*A^{-1}
A∗=∣A∣∗A−1,其中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=A−1
上面的时间复杂度都是
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;
}
本文介绍了矩阵的代数余子式及其计算方法,包括rank(A)为n-2、n和n-1的情况。当rank(A)=n时,提供了A*与A的行列式的乘积等于A的逆的性质。通过高斯消元法可求矩阵的秩,矩阵的逆可通过特定变换得到。最后,提到在实际问题中,如[Bzoj 3168],求逆矩阵后解决二分图最小字典序的完美匹配问题,时间复杂度为O(n^3)。
1211

被折叠的 条评论
为什么被折叠?



