洛谷P1119灾后重建

题目

做一个替我们首先要明确一下数据范围,n《=200,说明n^3的算法是可以过得,而且这个题很明显是一个图论题, 所以我们很容易想到这个题可以用folyd, 但是我在做这个题的时候因为没有深刻理解folyd的意义,因此导致调试了好几小时,

folyd 的思想是dp,我们可以用f[k][i][j]表示ij之间可以通过编号为1...k的节点的最短路径。

初值f[0][i][j]为原图的邻接矩阵。

f[k][i][j]可以从f[k-1][i][j]转移来,表示ij不经过k这个节点。
也可以从f[k-1][i][k]+f[k-1][k][j]转移过来,表示经过k这个点。
意思即f[k][i][j] = min(f[k-1][i][j] , f[k-1][i][k]+f[k-1][k][j])

然后你就会发现f最外层一维空间可以省略,因为f[k]只与f[k-1]有关。

虽然这个算法非常简单,但也需要找点时间理解这个算法,就不会再有这种问题啦。
因此我们在dp循环的时候可以使用滚动数组,可以节约点空间。
这时候我们就可以看回原题了,我们可以先想比较暴力点的算法,就是直接采用floyd,不加任何优化,这样会很慢,因此我们可以寻找可以优化的方法,比如题目中的时间是递增的,因此我们每次状态转移的时候并不用每次都从第一个点找,而是可以接着上一次的询问。
这样题目就一目了然了。
坑点:他的编号都是从0开始的,且初始化数组的时候不能开太大,否则会爆
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#define M 550
using namespace std;
int n, m, dis[M][M], t[M], now, ha, k;
int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
        {
            dis[i][j] = 214748364;
        }
    for(int i = 0; i < n; i++)
        scanf("%d", &t[i]);
    for(int i = 1; i <= m; i++)
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        dis[a][b] = c;
        dis[b][a] = c;
    }
    scanf("%d", &ha);
    for(int g = 1; g <= ha; g++)
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        for(k = now; k < n; k++)
        {
            if(t[k] > c)
                break;
            else
                for(int i = 0; i < n; i++)
                    for(int j = 0; j < n; j++)
                        dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
        }
        now = k++;
        if(dis[a][b] != 214748364 && t[a] <= c && t[b] <= c)
            printf("%d
", dis[a][b]);
        else
            printf("-1
");
    }
}
原文地址:https://www.cnblogs.com/liuwenyao/p/9612450.html