hdu 2433 Travel (最短路径树)

来源:互联网 发布:黄鹤tv武汉网络电视 编辑:程序博客网 时间:2024/06/05 20:32

hdu 2433 Travel

Description
One day, Tom traveled to a country named BGM. BGM is a small country, but there are N (N <= 100) towns in it. Each town products one kind of food, the food will be transported to all the towns. In addition, the trucks will always take the shortest way. There are M (M <= 3000) two-way roads connecting the towns, and the length of the road is 1.
Let SUM be the total distance of the shortest paths between all pairs of the towns. Please write a program to calculate the new SUM after one of the M roads is destroyed.

Input
The input contains several test cases.
The first line contains two positive integers N, M. The following M lines each contains two integers u, v, meaning there is a two-way road between town u and v. The roads are numbered from 1 to M according to the order of the input.
The input will be terminated by EOF.

Output
Output M lines, the i-th line is the new SUM after the i-th road is destroyed. If the towns are not connected after the i-th road is destroyed, please output “INF” in the i-th line.

Sample Input

5 4
5 1
1 3
3 2
5 4
2 2
1 2
1 2

Sample Output

INF
INF
INF
INF
2
2

题目大意:给你n个点m条边。现在按顺序一一删除这m条边(删除下一条边的时候,上一条边会恢复)。问删除这一条边以后,每两个点之间最短路径之和是多少,如果删除那条边之后图不连通,输出INF。

解题思路:不可能说删除每一条边,都再去跑n次dijkstra,这样肯定要超时的。

我们有n个点,也就是有n个起点,有n棵最短路径树。每次删除一条边的时候,我们就判断,这条边在哪些最短路径树上,然后减去这些最短路径树的起点所对应的边和,并且加上删除该边之后的以这些最短路径树对应的起点为起点的dijkstra求出的新的边和。在dijkstra的时候要判断,删除该边之后,图还连不连通,若不联通,输出INF,若联通处理完之后输出新的边和。最后要记得恢复这条边,然后继续删边。(写的有点拗口,看不懂看注释理解下)

#include <cstdio>#include <cstring>#include <algorithm>#include <cmath>#include <cstdlib>#include <queue>using namespace std; const int N = 105; const int M = 20005;const int INF = 0x3f3f3f3f;typedef long long ll;int n, m, cnt;int Dis[N];struct Edge{    int from, to, flag; };struct HeapNode{    int d,u;      bool operator < (const HeapNode& rhs) const{          return d > rhs.d;      }  };  struct Dijkstra{      int n,m;              //点数和边数      vector<Edge> edges;   //边列表      vector<int> G[M];     //每个结点出发的边编号(从0开始编号)      vector<int> tree[M];    bool done[N];         //是否已永久标号      int d[N];             //s到各个点的距离      int p[N];             //最短路中的上一条边      void init(int n) {          this->n = n;          for(int i = 0; i < 2 * m; i++) {            G[i].clear();//清空邻接表              tree[i].clear();        }        memset(p, -1, sizeof(p));        edges.clear();//清空边表      }      void addEdge(int from, int to) {           //如果是无向图,每条无向边需调用两次AddEdge          edges.push_back((Edge){from, to, 0});          edges.push_back((Edge){to, from, 0});        m = edges.size();          G[from].push_back(m - 2);          G[to].push_back(m - 1);    }      int dijkstra(int s) { //求s到所有点的距离          priority_queue<HeapNode> Q;          for(int i = 0; i <= n; i++) d[i] = INF;          d[s] = 0;          memset(done, 0, sizeof(done));          Q.push((HeapNode){0, s});          while(!Q.empty()){              HeapNode x = Q.top(); Q.pop();              int u = x.u;              if(done[u]) continue;              done[u] = true;              for(int i = 0; i < G[u].size(); i++){                  Edge& e = edges[G[u][i]];                  if (e.flag) continue; //判断该边是否被删除                if(d[e.to] > d[u] + 1){                      d[e.to] = d[u] + 1;                      p[e.to] = G[u][i];                     Q.push((HeapNode){d[e.to], e.to});                  }              }          }          int sum = 0;        for (int i = 1; i <= n; i++) { //求最短路径的边和            if (i == s) continue;            if (d[i] == INF) return -1;            sum += d[i];            }           return sum;    }      void getTree(int s) {        for (int i = 1; i <= n; i++)    {            if (i == s || p[i] == -1) continue;             tree[p[i]].push_back(s);        }    }    void killEdge(int x) {        edges[x].flag = 1;          edges[x^1].flag = 1;    }    void recoverEdge(int x) {        edges[x].flag = 0;        edges[x^1].flag = 0;    }}dij;  void input() {    int a, b;    for (int i = 0; i < m; i++) {        scanf("%d %d", &a, &b);             dij.addEdge(a, b);    }}void solve() {    memset(Dis, 0, sizeof(Dis));    int sum = 0;    for (int i = 1; i <= n; i++) {        Dis[i] = dij.dijkstra(i); //Dis记录以i为起点的最短路边之和,即d数组之和        if (Dis[i] == -1) {            sum = -1;               break;        }           sum += Dis[i];        dij.getTree(i);    }    if (sum == -1) { //原始图已不联通,可以直接pass了        for (int i = 0; i < m; i++) {            printf("INF\n");            }        return;    }    int temp;    for (int i = 0; i < dij.edges.size(); i++) { //因为是双向边,所以正反都要考虑。        dij.killEdge(i);    //杀死第i条边        if (i % 2 == 0) temp = sum; //正向边时,将原始的边和赋给temp        for (int j = 0; j < dij.tree[i].size(); j++) { //遍历最短路径上有第i条边的最短路径的起点            if (temp == -1) break;            temp -= Dis[dij.tree[i][j]]; //减去以tree[i][j]为起点的最短路径之和            int tft = dij.dijkstra(dij.tree[i][j]);            if (tft == -1) { //删除一边之后,图不连通                temp = -1;                break;              }            temp += tft; //加上删去第i条边之后的,新的边和        }        if (i % 2) {            if (temp == -1) printf("INF\n"); //图不连通            else printf("%d\n", temp); //新的边和        }        dij.recoverEdge(i); //复活第i条边    }}int main() {    while (scanf("%d %d", &n, &m) == 2) {        dij.init(n);            input();                solve();    }       return 0;}
0 0