1 /*
2 题意:给出一个N个城市组成的有向图,从i城市发送letter到j城市可以通过post/fiber,通过fiber发送需要0 hour,
3 通过post需要h hour,N个城市可以分为X个国家,组成国家的条件是几个城市之间可以通过post使得这几个城市强连
4 通,然后属于同一个国家的两个城市可以采取fiber的方式发送letter,并且属于同一个国家的城市任意两两之间会有
5 fiber相通,不同国家的城市之间则只能通过post发送,给出k个查询,问从a城市发送letter到b城市最短时间是多少
6
7 题解:强连通+缩点+最短路
8 此处的求有向图的强连通分量用的是kosaraju算法
9 显然同一个国家内发送letter只需要通过fiber,则时间消耗为0,因此,通过求强连通分量找出同一个国家的点,然后
10 缩点组成一个只能通过post发送消息的图,然后用最短路即可求出结果。
11 */
12 #include <cstdio>
13 #include <cstring>
14
15 const int MAXINT = 1e8;
16 const int MAXV = 505;
17
18 int g[MAXV][MAXV], dfn[MAXV], num[MAXV], n, scc, cnt; // 原图,后续遍历入栈顺序,记录强连通点,总点数,连通分量个数,指向栈的顶部
19 int map[MAXV][MAXV]; // 缩点后的图
20
21 void dfs(int k)
22 {
23 num[k] = 1;
24 for(int i=1; i<=n; i++)
25 if(g[k][i]!=-1 && !num[i])
26 dfs(i);
27 dfn[++cnt] = k; //记录第cnt个出栈的顶点为k
28 }
29 void ndfs(int k)
30 {
31 num[k] = scc; //本次DFS染色的点,都属于同一个scc,用num数组做记录
32 for(int i=1; i<=n; i++)
33 if(g[i][k] != -1 && !num[i]) //注意我们访问的原矩阵的对称矩阵
34 ndfs(i);
35 }
36 void kosaraju()
37 {
38 scc = 0;
39 cnt = 0;
40 memset(num,0,sizeof(num));
41 for(int i=1; i<=n; i++) //DFS求得拓扑排序
42 if(!num[i])
43 dfs(i);
44 memset(num, 0, sizeof(num));
45 /* 我们本需对原图的边反向,但由于我们使用邻接矩阵储存图,所以反向的图的邻接矩阵
46 即原图邻接矩阵的对角线对称矩阵,所以我们什么都不用做,只需访问对称矩阵即可*/
47 for(int i=n; i>=1; i--)
48 if(!num[dfn[i]])
49 { //按照拓扑序进行第二次DFS
50 scc++;
51 ndfs(dfn[i]);
52 }
53 }
54
55 void build_map() // 建图
56 {
57 for(int i=1; i<=scc; i++)
58 for(int j=1; j<=scc; j++)
59 map[i][j] = MAXINT;
60 for(int i=1; i<=n; i++)
61 for(int j=1; j<=n; j++)
62 if (g[i][j] != -1 && num[i] != num[j])
63 {
64 // 此处注意是num[i],因为是缩点后的图
65 if (map[num[i]][num[j]] == MAXINT || map[num[i]][num[j]] > g[i][j])
66 map[num[i]][num[j]] = g[i][j];
67 }
68 }
69
70 int dis[MAXV];
71 bool vis[MAXV];
72
73 void dij(int start,int end)
74 {
75 int min,min_f;
76 for(int i=1; i<=scc; i++)
77 {
78 dis[i] = map[start][i];
79 vis[i] = false;
80 }
81 vis[start] = true;
82 dis[start] = 0;
83 for(int i=1; i<scc; i++)
84 {
85 min=MAXINT;
86 min_f=1;
87 for(int j=1; j<=scc; j++)
88 {
89 if(!vis[j] && min>dis[j])
90 {
91 min=dis[j];
92 min_f=j;
93 }
94 }
95 vis[min_f]=true;
96 for(int j=1; j<=scc; j++)
97 {
98 if(!vis[j] && dis[j] > dis[min_f] + map[min_f][j])
99 dis[j] = dis[min_f] + map[min_f][j];
100 }
101 }
102 if(dis[end] == MAXINT)
103 printf("Nao e possivel entregar a carta\n");
104 else
105 printf("%d\n",dis[end]);
106 }
107 int main(void)
108 {
109 int e,k,o,d;
110 while (~scanf("%d%d",&n,&e) && n)
111 {
112 memset(g,-1,sizeof(g));
113 while (e--)
114 {
115 int u,v,w;
116 scanf("%d%d%d",&u,&v,&w);
117 if (-1 == g[u][v] || g[u][v] > w)
118 g[u][v] = w;
119 }
120 kosaraju();
121 build_map();
122 scanf("%d",&k);
123 while (k--)
124 {
125 scanf("%d%d",&o,&d);
126 if (o == d || num[o] == num[d])
127 printf("0\n");
128 else
129 {
130 // 此处注意是num[o],num[d],因为是缩点后的图
131 dij(num[o],num[d]);
132 }
133 }
134 printf("\n");
135 }
136 return 0;
137 }