[USACO07NOV] Cow Relays
Posted qixingzhi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[USACO07NOV] Cow Relays相关的知识,希望对你有一定的参考价值。
传送门:>Here<
题意:求在无向图中,S到E恰好经过T条边的最短路(边可重复走) ($T leq 100$)
解题思路
依然是好题。使用矩阵乘法——渐渐发现,矩阵乘法做图论题和Floyd有着很大的联系。从方程就能看出来相似:$f[i][k]+f[k][j]$和$f[i][k]*f[k][j]$。Floyd每次松弛一条边,最后拼凑出一条最短路。而邻接矩阵自乘k次相当于把自己松弛了k次——当然,和Floyd有所不同,Floyd如果无法松弛便不松弛,而矩阵乘法则会强行松弛k次,即使k次之后变成了0
这道题和上一道题不同,不再是纯矩阵乘法,而是怎么说,修改了乘法的定义。依然看邻接矩阵,$g[i][j]$表示的是$i$到$j$经过一条边的最短路。因此当我们做矩阵乘法时,将$ sumlimits_{i=1}^{cnt} g[i][k]*g[k][j] $ 改为 $Min{ g[i][k]+g[k][j] }$,假设得到的矩阵为b,那么$b[i][j]$在$g$“自乘”一次以后得到的就是$i到j$恰好经过两条边的最短路。因此通过$g^k$就能够求出经过k条边的。其本质依然是矩阵乘法快速幂。只不过自乘变为了“自加”的形式
Code
注意不能够添加判断$if(i==j) continue$,因为即使起点终点相等,绕一圈回到自己也是一种走法(就因为这个调了一个小时……)
另外,需要增加判断,转移时的$a[i][k]$与$a[k][j]$不能为0,因为我们需要保证严格的k条边。为0则代表那一部分不存在了……
/*By DennyQi*/ #include <cstdio> #include <queue> #include <cstring> #include <algorithm> #define r read() #define Max(a,b) (((a)>(b)) ? (a) : (b)) #define Min(a,b) (((a)<(b)) ? (a) : (b)) using namespace std; typedef long long ll; const int MAXN = 10010; const int MAXM = 27010; const int INF = 1061109567; inline int read(){ int x = 0; int w = 1; register int c = getchar(); while(c ^ ‘-‘ && (c < ‘0‘ || c > ‘9‘)) c = getchar(); if(c == ‘-‘) w = -1, c = getchar(); while(c >= ‘0‘ && c <= ‘9‘) x = (x << 3) + (x << 1) + c - ‘0‘, c = getchar(); return x * w; } int N,T,S,E,x,y,z,cnt; int g[105][105],ans[105][105],a[105][105],b[105][105],idx[1010]; inline void Matrix_KSM(int y){ while(y > 0){ if(y & 1){ for(int i = 1; i <= cnt; ++i){ for(int j = 1; j <= cnt; ++j){ b[i][j] = INF; for(int k = 1; k <= cnt; ++k){ if(!ans[i][k] || !a[k][j]) continue; b[i][j] = Min(b[i][j], ans[i][k] + a[k][j]); } } } for(int i = 1; i <= cnt; ++i){ for(int j = 1; j <= cnt; ++j){ ans[i][j] = b[i][j]; } } } for(int i = 1; i <= cnt; ++i){ for(int j = 1; j <= cnt; ++j){ b[i][j] = INF; for(int k = 1; k <= cnt; ++k){ if(!a[i][k] || !a[k][j]) continue; b[i][j] = Min(b[i][j], a[i][k] + a[k][j]); } } } for(int i = 1; i <= cnt; ++i){ for(int j = 1; j <= cnt; ++j){ a[i][j] = b[i][j]; } } y /= 2; } } int main(){ N = r, T = r, S = r, E = r; memset(g, 0x3f, sizeof(g)); for(int i = 1; i <= T; ++i){ z = r, x = r, y = r; if(!idx[x]) idx[x] = ++cnt; if(!idx[y]) idx[y] = ++cnt; g[idx[x]][idx[y]] = Min(g[idx[x]][idx[y]],z); g[idx[y]][idx[x]] = Min(g[idx[y]][idx[x]],z); } for(int i = 1; i <= cnt; ++i){ for(int j = 1; j <= cnt; ++j){ a[i][j] = g[i][j]; ans[i][j] = g[i][j]; } } Matrix_KSM(N-1); printf("%d", ans[idx[S]][idx[E]]); return 0; }
以上是关于[USACO07NOV] Cow Relays的主要内容,如果未能解决你的问题,请参考以下文章
[luoguP2886] [USACO07NOV]牛继电器Cow Relays(矩阵)
Luogu P2886 [USACO07NOV]牛继电器Cow Relays|最短路,倍增