POJ3114 Countries in War (强连通分量 + 缩点 + 最短路径 + 好题)

题目链接

题意是说在几个邮局之间传送一份信件,如果出发点和终止点在同一个国家传递,则时间为0,否则让你求花费最少时间,如果不能传到,则输出Nao e possivel entregar a carta。判断邮局是否在同一个国家的依据是发出的信件可以相互到达。
如果直接求最短路则无法判断两个邮局是否在同一个国家,判断两个邮局是否属于同一个国家的标志是在这个国家邮局间可以相互到达,那么这就是强连通了,所以要先缩点判读邮局是否在同一个国家,如果不是,则重新建图,建图的时候要维护好边权,求出最短边权,在用dijkstra求出最短路即可。
  1 #include <iostream>
  2 #include <cstring>
  3 #include <cstdio>
  4 #include <algorithm>
  5 #include <vector>
  6 using namespace std;
  7 const int Max = 505;
  8 const int INF = 0x3f3f3f3f;
  9 int n, m, dfs_clock, scc_cnt, scnt;
 10 int g[Max][Max], pre[Max], low[Max], Stack[Max], sccno[Max];
 11 int G[Max][Max];
 12 int head[Max], num;
 13 struct Edge
 14 {
 15     int v, Next;
 16 };
 17 Edge edge[Max * Max];
 18 void addEdge(int u, int v)
 19 {
 20     edge[num].v = v;
 21     edge[num].Next = head[u];
 22     head[u] = num++;
 23 }
 24 void init()
 25 {
 26     memset(head, -1, sizeof(head));
 27     memset(pre, 0, sizeof(pre));
 28     //memset(low, 0, sizeof(low));
 29     memset(sccno, 0, sizeof(sccno));
 30     scnt = dfs_clock = scc_cnt = num = 0;
 31     for (int i = 1; i <= n; i++)
 32         for (int j = i; j <= n; j++)
 33         {
 34             if (i == j)
 35                 G[i][j] = g[i][j] = 0;
 36             else
 37             {
 38                 g[i][j] = g[j][i] = INF;
 39                 G[i][j] = G[j][i] = INF;
 40             }
 41         }
 42 }
 43 void dfs(int u)
 44 {
 45     pre[u] = low[u] = ++dfs_clock;
 46     Stack[scnt++] = u;
 47     for (int i = head[u]; i != -1; i = edge[i].Next)
 48     {
 49         int v = edge[i].v;
 50         if (!pre[v])
 51         {
 52             dfs(v);
 53             low[u] = min(low[u], low[v]);
 54         }
 55         else if (!sccno[v])
 56             low[u] = min(low[u], pre[v]);
 57     }
 58     if (low[u] == pre[u])
 59     {
 60         scc_cnt++;
 61         for (; ;)
 62         {
 63             int x = Stack[--scnt];
 64             sccno[x] = scc_cnt;
 65             if ( x == u)
 66                 break;
 67         }
 68     }
 69 }
 70 void find_scc()
 71 {
 72     for (int i = 1; i <= n; i++)
 73     {
 74         if (!pre[i])
 75             dfs(i);
 76     }
 77 }
 78 void build_new_graphic()
 79 {
 80     for (int i = 1; i <= n; i++)
 81     {
 82         for (int j = 1; j <= n; j++)
 83         {
 84             if (i != j && sccno[i] != sccno[j] && g[i][j] != INF) // 不同的连通分量号建立一条有向边。
 85             {
 86                 G[ sccno[i] ][ sccno[j] ] = min(g[i][j], G[ sccno[i] ][ sccno[j] ]);
 87             }
 88         }
 89     }
 90 }
 91 int dist[Max], vis[Max];
 92 void dijkstra(int start, int goal)
 93 {
 94    //利用起点start,终点goal来搞,以前做惯了,直接用起点是1来做了
 95     for (int i = 1; i <= scc_cnt; i++)
 96         dist[i] = G[start][i]; 
 97     memset(vis, 0, sizeof(vis));
 98     dist[start] = 0;
 99     vis[start] = 1;
100     for (int i = 2; i <= scc_cnt; i++)
101     {
102         int minn = INF, pos = 1; // 这里初始化pos为1,否则当下面的循环不满足条件是,执行vis[pos]会出错
103         for (int j = 1; j <= scc_cnt; j++)
104         {
105             if (!vis[j] && minn > dist[j])
106             {
107                 minn = dist[j];
108                 pos = j;
109             }
110         }
111         vis[pos] = 1;
112         for (int j = 1; j <= scc_cnt; j++)
113         {
114             if (!vis[j] && dist[j] > dist[pos] + G[pos][j])
115                 dist[j] = dist[pos] + G[pos][j];
116         }
117     }
118     if (dist[goal] != INF)
119         printf("%d\n", dist[goal]);
120     else
121         printf("Nao e possivel entregar a carta\n");
122 }
123 int main()
124 {
125     while (scanf("%d%d", &n, &m) != EOF)
126     {
127         if (n == 0 && m == 0)
128             break;
129         init();
130         int u, v, c;
131         for (int i = 1; i <= m; i++)
132         {
133             scanf("%d%d%d", &u, &v, &c);
134             if (g[u][v] > c)
135             {
136                 g[u][v] = c;  // 判断重边
137             }
138             addEdge(u, v);
139         }
140         find_scc();  // 找强连通分量
141         //cout << scc_cnt << endl;
142         build_new_graphic();  // 重新构图
143 
144         int k;
145         scanf("%d", &k);
146         while (k--)
147         {
148             scanf("%d%d", &u, &v);
149             if (sccno[u] == sccno[v])  // 同一连通分量直接输出
150                 printf("0\n");
151             else
152             {
153                 dijkstra(sccno[u], sccno[v]);
154             }
155         }
156         printf("\n");
157     }
158 
159     return 0;
160 }
View Code

 

posted @ 2016-04-04 12:50  zhaop  阅读(289)  评论(0编辑  收藏  举报