BZOJ2200 道路和航线【好题】【dfs】【最短路】【缩点】
2200: [Usaco2011 Jan]道路和航线
Time Limit: 10 Sec Memory Limit: 259 MBSubmit: 1384 Solved: 508
[Submit][Status][Discuss]
Description
Input
Output
Sample Input
6 3 3 4
1 2 5
3 4 5
5 6 10
3 5 -100
4 6 -100
1 3 -10
样例输入解释:
一共六个城镇。在1-2,3-4,5-6之间有道路,花费分别是5,5,10。同时有三条航线:3->5,
4->6和1->3,花费分别是-100,-100,-10。FJ的中心城镇在城镇4。
Sample Output
NO PATH
NO PATH
5
0
-95
-100
样例输出解释:
FJ的奶牛从4号城镇开始,可以通过道路到达3号城镇。然后他们会通过航线达到5和6号城镇。
但是不可能到达1和2号城镇。
HINT
Source
题意:
$N$个点,$R$个无向边$P$个有向边。每条边上有一个权值,无向边的权值一定是正数,有向边可能为负。现在从$S$出发,问到达其他各点的最短路。
思路:
因为有负边,所以dijkstra不能用了。数据造的卡spfa了,据说用spfa优化可以过。
这里用的lyd书上说的解法,缩点然后拓扑序扫描求出单源最短路。复杂度是$O(T+P+RlogT)
由于无向边都是非负的,只有单向边可能是负的并且单向边不构成环。所以可以把图变成一个有向无环图。
首先我们添加所有的无向边,这会使得$N$个节点形成若干个联通块。我们把这些连通块整体看成一个点【缩点】。此时再添加有向边,就可以得到一张有向无环图。而在一个连通块内部我们可以使用堆优化的dijkstra计算块内的最短路。
算法大概分成这样几步:
1、使用dfs构建连通块,$vis[i]$记录的是第$i$个节点所在连通块的编号。
2、加入有向边,计算每个连通块的入度$deg[i]$
3、建立队列进行拓扑排序,最初时包含所有入度为$0$的连通块编号。初始化最短路数组$d$,$d[S] = 0$, 其他点为$inf$
4、使用堆优化的dijkstra处理每一个连通块。每次从堆中取出$d$最小的节点$x$,并且扫描$x$出发的所有边(包括无向和有向),进行松弛操作。
如果这条边的终点$y$和$x$在同一个连通块中,并且$d[y]$被$d[x]$更新了,就把$y$加入到堆中(与dijkstra相同)
如果终点和$x$不在同一个连通块中了,需要做的是拓扑排序的步骤。将$y$对应的连通块的入度减$1$,如果入度是0了,就可以插入到拓扑排序的队列末尾了。
虐狗宝典阅读笔记:
1、在有向无环图上,无论边权正负,都可以按照拓扑序进行扫描,在线性时间内求出单源最短路。
2、spfa一种名为SLF的优化策略:基于双端队列的思想。在每次更新$dist[y]$之后,把$dist[y]$与当前队头节点(即把$x$出队以后,队头的那个节点)的$dist$值进行比较。若$dist[y]$更小,则从队头把$y$入队,否则仍从队尾入队。
1 #include<iostream> 2 //#include<bits/stdc++.h> 3 #include<cstdio> 4 #include<cmath> 5 #include<cstdlib> 6 #include<cstring> 7 #include<algorithm> 8 #include<queue> 9 #include<vector> 10 #include<set> 11 #include<climits> 12 using namespace std; 13 typedef long long LL; 14 #define N 100010 15 #define pi 3.1415926535 16 #define inf 0x3f3f3f3f 17 18 int n, r, p, s; 19 const int maxn = 25005; 20 vector<pair<int, int> >road[maxn];//终点,权值 21 vector<pair<int, int> >flight[maxn]; 22 vector<int>seg[maxn]; 23 int vis[maxn]; 24 int deg[maxn];//第i个联通块的总入度 25 26 int cnt; 27 void dfs(int x) 28 { 29 vis[x] = cnt; 30 seg[cnt].push_back(x); 31 for(int i = 0; i < road[x].size(); i++){ 32 int y = road[x][i].first; 33 if(vis[y])continue; 34 dfs(y); 35 } 36 } 37 void get_connect() 38 { 39 cnt = 0;//联通块个数 40 for(int i = 1; i <= n; i++){ 41 vis[i] = 0; 42 } 43 for(int i = 1; i <= n; i++){ 44 if(!vis[i]){ 45 cnt++; 46 dfs(i); 47 } 48 } 49 } 50 51 LL d[maxn]; 52 bool v[maxn]; 53 void solve() 54 { 55 memset(v, 0, sizeof(v)); 56 memset(d, 0x3f, sizeof(d)); 57 d[s] = 0; 58 queue<int>q; 59 for(int i = 1; i <= cnt; i++){ 60 if(deg[i] == 0){ 61 q.push(i); 62 } 63 } 64 while(q.size()){ 65 int id = q.front();q.pop(); 66 set<pair<LL, int>, less<pair<LL, int> > >min_heap; 67 set<pair<LL, int>, less<pair<LL, int> > >::iterator it; 68 for(int i = 0; i < seg[id].size(); i++){ 69 min_heap.insert(make_pair(d[seg[id][i]], seg[id][i])); 70 //v[seg[id][i]] = 0; 71 } 72 while(!min_heap.empty()){ 73 it = min_heap.begin(); 74 int x = it->second; 75 min_heap.erase(*it); 76 //if(v[x])continue; 77 //v[x] = true; 78 for(int i = 0; i < road[x].size();i++){ 79 int y = road[x][i].first; 80 LL z = road[x][i].second; 81 if(d[x] + z < d[y]){ 82 if(vis[y] == vis[x]){ 83 min_heap.erase(make_pair(d[y], y)); 84 d[y] = d[x] + z; 85 min_heap.insert(make_pair(d[y], y)); 86 } 87 88 } 89 if(vis[x] != vis[y]){ 90 d[y] = min(d[y], d[x] + z); 91 deg[vis[y]]--; 92 if(deg[vis[y]] == 0)q.push(vis[y]); 93 } 94 } 95 for(int i = 0; i < flight[x].size();i++){ 96 int y = flight[x][i].first; 97 LL z = flight[x][i].second; 98 if(d[x] + z < d[y]){ 99 if(vis[y] == vis[x]){ 100 min_heap.erase(make_pair(d[y], y)); 101 d[y] = d[x] + z; 102 min_heap.insert(make_pair(d[y], y)); 103 } 104 105 } 106 if(vis[x] != vis[y]){ 107 d[y] = min(d[y], d[x] + z); 108 deg[vis[y]]--; 109 if(deg[vis[y]] == 0)q.push(vis[y]); 110 } 111 } 112 } 113 } 114 } 115 116 int main() 117 { 118 //freopen("in.txt", "r", stdin); 119 scanf("%d%d%d%d", &n, &r, &p, &s); 120 for(int i = 0; i <= n; i++){ 121 road[i].clear(); 122 flight[i].clear(); 123 seg[i].clear(); 124 } 125 for(int i = 0; i < r; i++){ 126 int u, v, w; 127 scanf("%d%d%d", &u, &v, &w); 128 road[u].push_back(make_pair(v, w)); 129 road[v].push_back(make_pair(u, w)); 130 } 131 get_connect(); 132 for(int i = 0; i < p; i++){ 133 int u, v, w; 134 scanf("%d%d%d", &u, &v, &w); 135 flight[u].push_back(make_pair(v, w)); 136 deg[vis[v]]++; 137 } 138 139 solve(); 140 for(int i = 1; i <= n; i++){ 141 if(d[i] > 1000000000){ 142 printf("NO PATH\n"); 143 } 144 else{ 145 printf("%lld\n", d[i]); 146 } 147 } 148 return 0; 149 }