poj 3114 强连通+缩点+最短路

  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 }

 

posted @ 2014-03-29 11:40  辛力啤  阅读(260)  评论(0编辑  收藏  举报