邻接矩阵运算的应用
矩阵的乘法、快速幂
struct Matrix {
int s[51][51];
Matrix() { memset(s,0,sizeof(s)); }
int *operator [](int x) { return s[x]; }
}M;
Matrix operator *(Matrix a,Matrix b) { // 矩阵乘法
Matrix c;
for(int k=1; k<=n; ++k)
for(int i=1; i<=n; ++i)
if(a[i][k])
for(int j=1; j<=n; ++j)
if(b[k][j])
(c[i][j]+=a[i][k]*b[k][j])%=mo;
return c;
}
Matrix operator ^(Matrix a,int b) { // 矩阵快速幂
Matrix ans;
for(int i=1; i<=n; ++i) ans[i][i]=1;
for(;b;b>>=1,a=a*a) if(b&1) ans=ans*a;
return ans;
}
1.[USACO07NOV]牛继电器Cow Relays
https://www.luogu.org/problemnew/show/P2886
题目大意:给出一张无向连通图,求S到E经过k条边的最短路
如果我们有两个矩阵
其中一个矩阵代表恰好经过x条边的最短路
另一个矩阵代表恰好经过y条边的最短路
那么将这两个矩阵相结合就代表恰好经过x+y条边的最短路
$ c[i][j]=min(c[i][j],a[i][k]+b[k][j]); $
这道题是一个模型:邻接矩阵求经过k条边的最短路(简称k边最短路)
2.[ZJOI2005]沼泽鳄鱼
https://www.luogu.org/problemnew/show/P2579
首先不看食人鱼的情况
我们要求从s到t可以经过k条边(可以经过重复点)的方案数
这个是邻接矩阵运算最擅长的
与k边最短路不同的是
方案数是根据乘法原理得来的
所以我们在求解的时候要用到矩阵乘法
那么再把食人鱼放入到池塘里
发现食人鱼的周期奇短无比,都是12的因子
很小,所以我们可以构造出12个邻接矩阵。
利用这12个邻接矩阵
便既可以完全的表示出食人鱼的行动周期
也可以统计人活动的情况周期
就让食人鱼在某一时间点所在的地点情况数清零即可
然后以12为周期进行矩阵乘法
非常好的一道邻接矩阵的题目。
模型:求一个点到另一个点经过k条边的方案数(简称st-k方案数)
3.[TJOI2017]可乐
https://www.luogu.org/problemnew/show/P3758
这道题的构造巧妙无比
先不看有爆炸,可以停留的情况,这道题就变成了st-k方案数,只不过这里是从1出发,到1在内的所有节点的方案数
再来看可以停留这个条件。不就是从一个点走到自己吗?所以,再邻接矩阵中,我们只需要让一个点从自己连到自己就可以了。
爆炸怎么办?我们可以新开一个节点n+1,所有点向n+1连一条边,而它不向除自己以外的任何点连边。为什么n+1要向自己连边呢?这就像在一个点停留一样。如果不向自己连边,那么在下一个矩阵中它将不复存在。
总结
这三道题都是十分经典的邻接矩阵运算题目
都有着非常好的价值和意义
可以说
如果完全掌握了这三道题
那么邻接矩阵的运算可以说是轻车熟驾了。
code
T1
// luogu-judger-enable-o2
#include
#include
#include
#include
#include
#include
#define rg register int
#define ll long long
#define RG register
#define il inline
using namespace std;
il int gi() {
rg x=0,o=0;RG char ch=getchar();
while(ch!='-'&&(ch<'0'||'9'>=1;
}
}
int main() {
n=gi(),m=gi(),s=gi(),t=gi();
memset(mp,-1,sizeof(mp));
memset(ans.a,0x3f,sizeof(ans.a));
memset(dis.a,0x3f,sizeof(dis.a));
for(rg u,v,w,i=1; i<=m; ++i)
{
w=gi(),u=gi(),v=gi();
if(mp[u]==-1) mp[u]=++cnt;
if(mp[v]==-1) mp[v]=++cnt;
u=mp[u],v=mp[v];
Getmin(dis.a[u][v],w);
dis.a[v][u]=dis.a[u][v];
}
power(n);
printf("%lld",ans.a[mp[s]][mp[t]]);
return 0;
}
T2
#include
#include
#include
#include
#include
#include
#define rg register int
#define ll long long
#define RG register
#define il inline
using namespace std;
il int gi() {
rg x=0,o=0;RG char ch=getchar();
while(ch!='-'&&(ch<'0'||'9'>=1,a=a*a) if(b&1) ans=ans*a;
return ans;
}
int p[5];
int main() {
N=gi(),M=gi(),S=gi()+1,T=gi()+1,K=gi();
// 因为题目给出的是从 0 开始,所以我们要+1 来强制 从1开始
for(rg x,y,i=1; i<=M; ++i) {
x=gi()+1,y=gi()+1;
for(rg j=1; j<=12; ++j)
dis[j][x][y]=dis[j][y][x]=1;
}
FN=gi();
for(rg i=1; i<=FN; ++i) {
p[0]=gi();
for(rg j=1; j<=p[0]; ++j) p[j]=gi();
for(rg j=1; j<=12; ++j)
for(rg pos=j%p[0]+1,k=1; k<=N; ++k)
dis[j][k][p[pos]+1]=0;
}
for(rg i=1; i<=N; ++i) dis[0][i][i]=1;
for(rg i=1; i<=12; ++i) dis[0]=dis[0]*dis[i];
Matrix Ans=dis[0]^(K/12);
for(rg i=1; i<=K%12; ++i) Ans=Ans*dis[i];
printf("%d",Ans.s[S][T]);
return 0;
}
T3
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include