poj3613题解

  • 题目大意
    给定一个图,有T( T<=100 )条无向边,求从S到E恰经N条边的最短路径。点数tot不大于1000。

  • 题解
    没想到矩阵乘法加快速幂还有这种用处。这种解法本质上是dp,用f[k][i][j]表示恰经过k条边时i到j的最短路,则有:
    f(k,i,j)=min(f(kp,i,temp)+f(p,temp,j))
    但暴力显然要T,所以使用倍增优化。用F[k][i][j]表示恰经 2k 条边时i到j的最短路,则有:
    F(k,i,j)=min(f(k1,i,temp)+f(k1,temp,j)
    i到j经过 <=2k <script type="math/tex" id="MathJax-Element-117"><=2^k</script>条边的最短路g[k][i][j]是:
    g(k,i,j)=min(g(k1,i,j),f(k,i,j))
    那么i到j经过N条边的最短路可以把N进行二进制分解,然后用矩阵乘法加快速幂解决。这里矩阵加法应替换为求最小值,矩阵乘法替换为求加法。时间复杂度 O(T3log2N) ,符合要求。

  • Code:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 105, maxp = 1005, oo = -1;
int N, T, S, E;
int h[maxp], sum;
struct matrix {
    int data[maxn][maxn];
    int n;
    matrix(int n = 0) :n(n) { memset(data, oo, sizeof(data)); }
    matrix operator * (matrix &b) {
        matrix c = matrix(n);
        for(int i = 1; i <= n; ++i) for(int j = 1; j <= n; ++j)
        for(int k = 1; k <= n; ++k) {
            if(data[i][k] == oo || b.data[k][j] == oo) continue;
            if(c.data[i][j] == oo || c.data[i][j] > data[i][k] + b.data[k][j])
                c.data[i][j] = data[i][k] + b.data[k][j];
        }
        return c;
    }
    matrix operator ^ (int k) {
        matrix t = (*this);
        while(k != 0) {
            if(k & 1) (*this) = (*this)*t;
            k >>= 1;
            t = t*t;
        }
        return (*this);
    }
}g;
void read(int &a) {
    int f = 1;
    char c = getchar();
    a = 0;
    while(c < '0' || c > '9') {
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9') {
        a = a*10 + c - 48;
        c = getchar();
    }
    a *= f;
}
void write(int a) {
    int t = 0;
    char c[30];
    if(a < 0) { putchar('-'); a = -a; }
    do {
        c[t++] = a%10 + 48;
        a /= 10;
    } while(a);
    while(t--) putchar(c[t]);
    putchar('\n');
}
void init() {
    int s, t, l, maxm = 0;
    read(N); read(T); read(S); read(E);
    memset(h, oo, sizeof(h));
    g = matrix(100);
    for(int i = 1; i <= T; ++i) {
        read(l); read(s); read(t);
        if(h[s] == oo) h[s] = ++sum;
        if(h[t] == oo) h[t] = ++sum;
        g.data[h[s]][h[t]] = g.data[h[t]][h[s]] = l;
    }
}
void work() {
    g = (g^(N-1));//上面从1次方开始乘,所以这里N-1
    write(g.data[h[S]][h[E]]);
}
int main() {
    init();
    work();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值