[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的主要内容,如果未能解决你的问题,请参考以下文章

[USACO07NOV]牛继电器Cow Relays

[USACO07NOV]牛继电器Cow Relays

[luoguP2886] [USACO07NOV]牛继电器Cow Relays(矩阵)

Luogu P2886 [USACO07NOV]牛继电器Cow Relays|最短路,倍增

奶牛接力 (Cow Relays, USACO 2007 Nov)

洛谷 P2888 [USACO07NOV]牛栏Cow Hurdles