HDU 2807 The Shortest Path 【最短路 + 快速判断矩阵是否相等】

传送门
// 题意: 给定n个m*m的矩阵, 如果A*B == C矩阵, 那么A -> C 有一条长度为1 的边. 最后给出q次询问, 每次询问两个矩阵的最短距离.

// 思路: 很明显我们首先需要先把矩阵先存下来, 然后n^3方建图, 然后我们知道矩阵相乘是n^3的,然后判断两个矩阵是否相等是n^2的, 一共就是n^5的,完全会爆炸. 所以这样肯定不行. 所以我们首先要对矩阵进行hash降维判断. 使得判断是O(n)的, 这样相乘也变成n^2了, n^4的复杂度就可以过了. 如何操作了. 首先将每一个行的每一个数字乘上对应的列标在累加存到该行的值中(mp[i] = nj=1a[i][j]j ), 然后两个矩阵相乘相乘判断是否等于对应目标矩阵的那行(假设是第2行), 就等于 ni=1a[2][i]mp[2] (这个mp[2]是B矩阵第二行hash值)是否等于目标矩阵的 mp[2] 就行啦. 也就是只用判断行数了, 就降到O(n)啦.
证明如下:
假设目标矩阵的第i行元素是 ci1 , …., cin , 那么 mp[i]=ci1i+...+cinn .
tmp=ci1i+...+cinn 又因为 ci1=nk=1a[i][k]b[k][1]
所以 tmp=nk=1a[i][k]b[k][1]1+....+nk=1a[i][k]b[k][n]n
在所以 tmp=nk=1a[i][k](b[k][1]1+....+b[k][n]n)
然后 tmp=nk=1a[i][k]mp[k] // 这里的mp[k]是B矩阵第k行的hash值.
所以才有的我们O(n)的判断矩阵.

// 注意: A*B == C 这三个矩阵必须互异. 所以我们建图的时候i -> i 是inf 不是 0 . inf就是不可达. 然后跑floyd然后O(1)的回答q个问题就行啦.

// 这份代码也可做快速判断矩阵相等的模板.

AC Code

const int maxn = 80+5;
int cas=1;
int g[maxn][maxn];
struct Ma{
    int mat[maxn][maxn];
    int n, m;
    void cc() { Fill(mat, 0); }
    void set_size(int row, int col) { n = row; m = col; }
    Ma& operator = (const Ma &a ) {
        set_size(a.n, a.m);
        memcpy(mat, a.mat, sizeof(a.mat));
        return *this;
    }
    friend Ma operator * (const Ma &a, const Ma &b) {
        Ma tmp; tmp.cc(); tmp.set_size(a.n, b.m);
        for(int i = 1 ; i <= a.n ; i ++) {
            for(int j = 1 ; j <= b.m ; j ++) {
                for(int k = 1 ; k <= b.n ; k ++) {
                    if(!a.mat[i][k] || !b.mat[k][j])  continue;
                    tmp.mat[i][j] += (a.mat[i][k] * b.mat[k][j]);
                }
            }
        }
        return tmp;
    }
    void pfma() {
        for (int i = 1 ; i <= n ; i ++) {
            for (int j = 1 ; j <= m ; j ++) {
                printf("%d%c", mat[i][j], j == m?'\n':' ');
            }
        }
    }
}arr[maxn];

struct hashMa {
    int mp[maxn];
    void cc() { Fill(mp, 0); }
    void cal(Ma& a, hashMa& b) {
        cc();
        for (int i = 1 ; i <= a.n ; i ++) {
            for (int j = 1 ; j <= a.m ; j ++) {
                mp[i] += a.mat[i][j] * b.mp[j];
            }
        }
    }
}res[maxn];

bool ok(hashMa a, hashMa b, int len) {
    for (int i = 1 ; i <= len ; i ++) {
        if (a.mp[i] != b.mp[i]) return false;
    }
    return true;
}

void solve()
{
    int n, m;
    while(~scanf("%d%d",&n, &m)) {
        if (n + m == 0) break;
        for (int i = 1 ; i <= n ; i ++) {
            for (int j = 1 ; j <= n ; j ++) {
                g[i][j] = inf;
            }
        }
        for (int i = 1 ; i <= n ; i ++) {
            arr[i].set_size(m, m); res[i].cc();
            for (int j = 1 ; j <= m ; j ++) {
                for (int k = 1 ; k <= m ; k ++) {
                    scanf("%d", &arr[i].mat[j][k]);
                    res[i].mp[j] += arr[i].mat[j][k] * k;
                }
            }
        }
        for (int i = 1 ; i <= n ; i ++) {
            for (int j = 1 ; j <= n ; j ++) {
                if (i == j) continue;
                hashMa tmp;
                tmp.cal(arr[i], res[j]);
                for (int k = 1 ; k <= n ; k ++) {
                    if (k == i || k == j) continue;
                    if (ok(tmp, res[k], m)) {
                        g[i][k] = 1;
                    }
                }
            }
        }
        for (int k = 1 ; k <= n ; k ++)
            for (int i = 1 ; i <= n ; i ++)
                for (int j = 1 ; j <= n ; j ++)
                    g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
        int q; scanf("%d", &q);
        while(q--) {
            int st, ed;
            scanf("%d%d", &st, &ed);
            if (g[st][ed] == inf) cout << "Sorry" << endl;
            else cout << g[st][ed] << endl;
        }
    }
}

你可能感兴趣的:(最短路相关,矩阵快速幂/快速乘)