大白叔叔专题之最短路、最小生成树(二)
1、uva 11374 Airport Express
题意:给出若干经济舱的路线和商务舱的路线,但只能选择乘坐一次商务舱。求前往机场的最短时间。
思路:分别从起点和终点求出到其他点的最短路,枚举每个商务舱,记录最小值。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<vector> 3 #include<queue> 4 #include<cstring> 5 #include<algorithm> 6 #include<cstdio> 7 using namespace std; 8 const int maxn = 510; 9 const int INF = 0x3f3f3f3f; 10 struct edge 11 { 12 int from, to, cost,next; 13 edge(int ff=0,int tt=0,int cc=0,int nn=0):from(ff),to(tt),cost(cc),next(nn){ } 14 }; 15 vector<edge>Edge; 16 int Head[maxn], totedge; 17 int N, S, E; 18 int M, K; 19 struct DIJ 20 { 21 bool vis[maxn]; 22 int preS[maxn]; 23 int disS[maxn]; 24 int disT[maxn]; 25 int preT[maxn]; 26 int st, ed,n; 27 void set(int nodes,int source,int dest) 28 { 29 n=nodes,st = source, ed = dest; 30 } 31 void Init() 32 { 33 memset(Head, -1, sizeof(Head)); 34 totedge = 0; 35 Edge.clear(); 36 } 37 void addedge(int from, int to, int cost) 38 { 39 Edge.push_back(edge(from, to, cost, Head[from])); 40 Head[from] = totedge++; 41 Edge.push_back(edge(to, from, cost, Head[to])); 42 Head[to] = totedge++; 43 } 44 void cal_dij(int *dis,int *pre) 45 { 46 memset(vis, 0, sizeof(vis)); 47 for (int i = 1; i <= n; i++) dis[i] = INF,pre[i]=-1; 48 for (int i = Head[st]; i != -1; i = Edge[i].next) 49 { 50 dis[Edge[i].to] = Edge[i].cost; 51 pre[Edge[i].to] = st; 52 } 53 vis[st] = true, dis[st] = 0; 54 for (int i = 1; i < n; i++) 55 { 56 int u = st, Min = INF; 57 for (int j = 1; j <= n; j++) 58 { 59 if (!vis[j] && dis[j] < Min) u = j, Min = dis[j]; 60 } 61 vis[u] = true; 62 for (int k = Head[u]; k != -1; k = Edge[k].next) 63 { 64 int v = Edge[k].to; 65 if (!vis[v] && dis[v] > dis[u] + Edge[k].cost) 66 { 67 dis[v] = dis[u] + Edge[k].cost; 68 pre[v] = u; 69 } 70 } 71 } 72 } 73 void PrintS(int u) 74 { 75 if (u != S) PrintS(preS[u]); 76 if (u == S) printf("%d",u); 77 else printf(" %d", u); 78 } 79 void PrintT(int u) 80 { 81 printf(" %d", u); 82 if(u!=E) PrintT(preT[u]); 83 } 84 }dij; 85 86 int main() 87 { 88 int Case = 1; 89 while (~scanf("%d%d%d", &N, &S, &E)) 90 { 91 if (Case > 1) printf("\n"); 92 scanf("%d", &M); 93 dij.Init(); 94 for (int i = 1; i <= M; i++) 95 { 96 int u, v, c; 97 scanf("%d%d%d", &u, &v, &c); 98 dij.addedge(u, v, c); 99 } 100 dij.set(N, S, E); 101 dij.cal_dij(dij.disS,dij.preS); 102 dij.set(N, E, S); 103 dij.cal_dij(dij.disT,dij.preT); 104 scanf("%d", &K); 105 int ans = dij.disS[E],from,to; 106 for(int i=1;i<=K;i++) 107 { 108 int u, v, c; 109 scanf("%d%d%d", &u, &v, &c); 110 if (dij.disS[u] + dij.disT[v] + c < ans) ans = dij.disS[u] + dij.disT[v] + c, from = u, to = v; 111 if (dij.disS[v] + dij.disT[u] + c < ans) ans = dij.disS[v] + dij.disT[u] + c, from = v, to = u; 112 } 113 if (ans == dij.disS[E]) 114 { 115 dij.PrintS(E); 116 printf("\n"); 117 printf("Ticket Not Used\n"); 118 printf("%d\n", ans); 119 } 120 else 121 { 122 dij.PrintS(from); 123 dij.PrintT(to); 124 printf("\n"); 125 printf("%d\n", from); 126 printf("%d\n", ans); 127 } 128 Case++; 129 } 130 return 0; 131 }
2、uva 10917 Walk Through the Forest
题意:办公室在1,家在2.现在需要从办公室回到家,途中会穿过森林中的n-2个结点。给出无向图,如果当前位置再结点u,存在从u-->v的边并且v到家的最短距离小于u到家的距离,则可以从u走到v.现在求所有的方案数。
思路:先求出所有的点到家的最短距离。然后用dfs得到方案数(类似深搜一棵有根外向树)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<queue> 3 #include<cstring> 4 #include<cstdio> 5 #include<vector> 6 using namespace std; 7 const int maxn = 1100; 8 const int INF = 0x3f3f3f3f; 9 struct edge 10 { 11 int from, to, cap,next; 12 edge(int ff=0,int tt=0,int cc=0,int nn=0):from(ff),to(tt),cap(cc),next(nn){ } 13 }; 14 vector<edge>Edge; 15 int Head[maxn], totedge; 16 struct SPFA 17 { 18 int n, st, ed; 19 int vis[maxn]; 20 int dis[maxn]; 21 int cnt[maxn]; 22 int pre[maxn]; 23 void Init() 24 { 25 memset(Head, -1, sizeof(Head)); 26 totedge = 0; 27 Edge.clear(); 28 } 29 void set(int nodes, int source, int dest) 30 { 31 n = nodes, st = source, ed = dest; 32 } 33 void addedge(int from, int to, int cap) 34 { 35 Edge.push_back(edge(from, to, cap, Head[from])); 36 Head[from] = totedge++; 37 Edge.push_back(edge(to, from, cap, Head[to])); 38 Head[to] = totedge++; 39 } 40 bool cal_spfa() 41 { 42 memset(vis, 0, sizeof(vis)); 43 memset(dis, INF, sizeof(dis)); 44 memset(cnt, 0,sizeof(cnt)); 45 memset(pre, -1, sizeof(pre)); 46 47 vis[st] = true, cnt[st]++, dis[st] = 0; 48 queue<int>q; 49 q.push(st); 50 while (!q.empty()) 51 { 52 int u = q.front(); 53 q.pop(); 54 vis[u] = false; 55 56 for (int i = Head[u]; i != -1; i = Edge[i].next) 57 { 58 int v = Edge[i].to; 59 if (dis[v] > dis[u] + Edge[i].cap) 60 { 61 dis[v] = dis[u] + Edge[i].cap; 62 pre[v] = u; 63 if (!vis[v]) 64 { 65 vis[v] = true; 66 q.push(v); 67 cnt[v]++; 68 if (v > n)return false; 69 } 70 } 71 } 72 } 73 return true; 74 } 75 }spfa; 76 int n,m; 77 int Count[maxn]; 78 int dfs(int u) 79 { 80 if (Count[u] != 0) return Count[u]; 81 for (int i = Head[u]; i != -1; i = Edge[i].next) 82 { 83 int v = Edge[i].to; 84 if (spfa.dis[v]<spfa.dis[u]) 85 { 86 int tmp = dfs(v); 87 Count[u] += tmp; 88 } 89 } 90 return Count[u]; 91 } 92 int main() 93 { 94 while (~scanf("%d", &n) && n) 95 { 96 int from, to, d; 97 spfa.Init(); 98 scanf("%d", &m); 99 for (int i = 1; i <= m; i++) 100 { 101 scanf("%d%d%d", &from, &to, &d); 102 spfa.addedge(from, to, d); 103 } 104 spfa.set(n, 2, 1); 105 spfa.cal_spfa(); 106 memset(Count, 0, sizeof(Count)); 107 Count[2] = 1; 108 int ans = dfs(1); 109 printf("%d\n", ans); 110 } 111 return 0; 112 }
3、uva 10537 The Toll! Revisited
题意:现在要求运到目的地时要有若干货物。从一个地方离开不需要缴纳费用,但进入一个地方时,需要缴纳费用,如果是village(小写字母表示),只需缴纳一个货物作为费用;如果是town(大写字母表示),每20个货物需要缴纳1个货物作为费用。问出发时最少需要的货物,并给出字典序最小的路径。
思路:因为边的费用和带的货物的数目有关,逆向求解,如果逆向从u到若干其他结点,需要考虑u是viliage还是town,前者为dis[u]+1,后者为ceil(dis[u]*20.0/19.0).然后记录路径前驱,方便输出。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<algorithm> 3 #include<queue> 4 #include<vector> 5 #include<cstring> 6 #include<cctype> 7 #include<cmath> 8 using namespace std; 9 const int maxn = 110; 10 const long long INF = (long long)1<<61; 11 struct edge 12 { 13 int from, to, next; 14 edge(int ff=0,int tt=0,int nn=0):from(ff),to(tt),next(nn){ } 15 }; 16 vector<edge>Edge; 17 int Head[maxn], totedge; 18 19 struct SPFA 20 { 21 int n, st, ed; 22 long long dis[maxn]; 23 int cnt[maxn]; 24 int vis[maxn]; 25 int pre[maxn]; 26 27 void Init() 28 { 29 memset(Head, -1, sizeof(Head)); 30 totedge = 0; 31 Edge.clear(); 32 } 33 void set(int nodes, int source, int dest) 34 { 35 n = nodes, st = source, ed = dest; 36 } 37 void addedge(int from, int to) 38 { 39 Edge.push_back(edge(from, to, Head[from])); 40 Head[from] = totedge++; 41 Edge.push_back(edge(to, from, Head[to])); 42 Head[to] = totedge++; 43 } 44 bool cal_spfa(int desnum) 45 { 46 memset(vis, 0, sizeof(vis)); 47 memset(dis, 0x3f, sizeof(dis)); 48 memset(pre, -1, sizeof(pre)); 49 memset(cnt, 0, sizeof(cnt)); 50 queue<int>q; 51 q.push(ed); 52 vis[ed] = true, dis[ed] = desnum; 53 while (!q.empty()) 54 { 55 int u = q.front(); 56 q.pop(); 57 vis[u] = false; 58 long long c; 59 if (u >= 26) 60 { 61 c = (long long)ceil(dis[u]*1.0/19.0); 62 //c = (long long)ceil(double(dis[u])*20.0 / 19.0) - dis[u]; 63 } 64 else c = 1; 65 for (int i = Head[u]; i != -1; i = Edge[i].next) 66 { 67 int v = Edge[i].to; 68 if (dis[v] > dis[u] + c) 69 { 70 dis[v] = dis[u] + c; 71 pre[v] = u; 72 if (!vis[v]) 73 { 74 q.push(v); 75 vis[v] = true; 76 cnt[v]++; 77 } 78 } 79 else if (dis[v] == dis[u] + c) 80 { 81 if (u > pre[v]&&u>=26&&pre[v]<26) pre[v] = u; 82 else if (u < pre[v]&&pre[v]<26) pre[v] = u; 83 else if(u<pre[v]&&u>=26)pre[v] = u; 84 } 85 } 86 } 87 return true; 88 } 89 }spfa; 90 int n, desnum, st, ed; 91 int main() 92 { 93 char u[4],v[4]; 94 int Case = 1; 95 while (~scanf("%d", &n) && n != -1) 96 { 97 spfa.Init(); 98 for (int i = 1; i <= n; i++) 99 { 100 scanf("%s%s", u, v); 101 int ui, vi; 102 if (isupper(u[0])) ui = u[0] - 'A' + 26; 103 else ui = u[0] - 'a'; 104 if (isupper(v[0])) vi = v[0] - 'A' + 26; 105 else vi = v[0] - 'a'; 106 spfa.addedge(ui, vi); 107 } 108 scanf("%d%s%s", &desnum, u, v); 109 110 if (isupper(u[0])) st = u[0] - 'A' + 26; 111 else st = u[0] - 'a'; 112 if (isupper(v[0])) ed = v[0] - 'A' + 26; 113 else ed = v[0] - 'a'; 114 115 spfa.set(60, st, ed); 116 spfa.cal_spfa(desnum); 117 printf("Case %d:\n", Case++); 118 printf("%lld\n", spfa.dis[st]); 119 int tmp =st; 120 while (true) 121 { 122 if (tmp == ed) 123 { 124 printf("%c\n", v[0]); 125 break; 126 } 127 char p; 128 if (tmp < 26) p = 'a' + tmp; 129 else p = 'A' + (tmp - 26); 130 printf("%c-", p); 131 tmp = spfa.pre[tmp]; 132 } 133 } 134 return 0; 135 }
4、uva 11090 Going in Cycle!!
题意:给出有环有向图,求所存在的环中边权平均值最小是多少?
思路:二分答案,如果所有边都减去当前值,SPFA判断存在负环,则说明当前值太大,最小平均值肯定比它要小;如果不存在负环,则说明减的不够大,最小平均值一定比其大。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<algorithm> 3 #include<queue> 4 #include<cstring> 5 #include<vector> 6 using namespace std; 7 const int maxn = 60; 8 const int INF = 0x3f3f3f3f; 9 struct edge 10 { 11 int from, to, next; 12 double cost; 13 edge(int ff=0,int tt=0,int cc=0,int nn=0):from(ff),to(tt),cost(cc),next(nn){ } 14 }; 15 vector<edge>Edge; 16 int Head[maxn], totedge; 17 int N, M; 18 19 20 struct SPFA 21 { 22 int n, st; 23 bool vis[maxn]; 24 double dis[maxn]; 25 int cnt[maxn]; 26 bool visn[maxn]; 27 void Init() 28 { 29 memset(Head, -1, sizeof(Head)); 30 totedge = 0; 31 Edge.clear(); 32 } 33 void set(int nodes, int source) 34 { 35 n = nodes, st = source; 36 } 37 void addedge(int from, int to, double cap) 38 { 39 Edge.push_back(edge(from, to, cap, Head[from])); 40 Head[from] = totedge++; 41 } 42 bool cal_spfa() 43 { 44 memset(vis, 0, sizeof(vis)); 45 memset(dis, INF, sizeof(dis)); 46 memset(cnt, 0, sizeof(cnt)); 47 48 queue<int>q; 49 q.push(st); 50 vis[st] = true; 51 cnt[st]++; 52 dis[st] = 0; 53 while (!q.empty()) 54 { 55 int u = q.front(); 56 q.pop(); 57 visn[u] = true; 58 vis[u] = false; 59 for (int i = Head[u]; i != -1; i = Edge[i].next) 60 { 61 int v = Edge[i].to; 62 if (dis[v] > dis[u] + Edge[i].cost) 63 { 64 dis[v] = dis[u] + Edge[i].cost; 65 if (!vis[v]) 66 { 67 vis[v] = true; 68 q.push(v); 69 cnt[v]++; 70 if (cnt[v] > n) return false; 71 } 72 } 73 } 74 } 75 return true; 76 } 77 bool binsolve(double mid) 78 { 79 for (int i = 0; i < totedge; i++) Edge[i].cost -= mid; 80 memset(visn, 0, sizeof(visn)); 81 bool isok = true; 82 for (int i = 1; i <= N; i++) 83 { 84 if (!visn[i]) 85 { 86 set(N, i); 87 isok = cal_spfa(); 88 if (!isok) break; 89 } 90 } 91 for (int i = 0; i < totedge; i++) Edge[i].cost += mid; 92 return isok; 93 } 94 }spfa; 95 96 int main() 97 { 98 int t; 99 scanf("%d", &t); 100 int Case = 1; 101 while (t--) 102 { 103 double L=INF, R=0; 104 scanf("%d%d", &N, &M); 105 spfa.Init(); 106 for (int i = 1; i <= M; i++) 107 { 108 int from, to; 109 double cost; 110 scanf("%d%d%lf", &from, &to, &cost); 111 spfa.addedge(from, to, cost); 112 L = min(L, cost); 113 R = max(R, cost); 114 } 115 if (spfa.binsolve(R+1000)) 116 { 117 printf("Case #%d: No cycle found.\n", Case++); 118 continue; 119 } 120 while (R - L > 1e-6) 121 { 122 double mid = (R + L) / 2; 123 if (!spfa.binsolve(mid)) R = mid; 124 else L = mid; 125 } 126 printf("Case #%d: %.2lf\n", Case++, L); 127 } 128 return 0; 129 }
5、uva 11478 Halum
题意:给出有向图,每一次可以把连向u的所有的边的边权减去di,把从u出发连向其他点的边的边权加上di.问经过若干次操作后,能否使图中所有边的边权都大于0,如果可以给出其最小值。
思路:对于某一条有向边u--->v,设其边权为w,作用在该边上的最终结果为w+sum(u)-sum(v)>=ans.即sum(v)-sum(u)<=w-ans.即转换为差分约束问题。于是我们二分答案,让所有的边减去ans,然后判断是否存在负环,没有的话说明ans还可以增大,否则只能减小。注意这里的边权和di都是整数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<algorithm> 3 #include<queue> 4 #include<vector> 5 #include<cstring> 6 using namespace std; 7 const int maxn = 510; 8 const int INF = 0x3f3f3f3f; 9 struct edge 10 { 11 int from, to, cost, next; 12 edge(int ff=0,int tt=0,int cc=0,int nn=0):from(ff),to(tt),cost(cc),next(nn){ } 13 }; 14 vector<edge>Edge; 15 int Head[maxn], totedge; 16 struct SPFA 17 { 18 int n, st, ed; 19 int cnt[maxn]; 20 bool vis[maxn]; 21 int dis[maxn]; 22 23 void Init() 24 { 25 memset(Head, -1, sizeof(Head)); 26 totedge = 0; 27 Edge.clear(); 28 } 29 void addedge(int from, int to, int cost) 30 { 31 Edge.push_back(edge(from, to, cost, Head[from])); 32 Head[from] = totedge++; 33 } 34 bool cal_spfa(int mid) 35 { 36 memset(cnt, 0, sizeof(cnt)); 37 queue<int>q; 38 for (int i = 1; i <= n; i++) q.push(i), dis[i] = 0, vis[i] = true,cnt[i]=1; 39 while (!q.empty()) 40 { 41 int u = q.front(); 42 q.pop(); 43 vis[u] = false; 44 for (int i = Head[u]; i != -1; i = Edge[i].next) 45 { 46 int v = Edge[i].to; 47 if (dis[v] > dis[u] + Edge[i].cost - mid) 48 { 49 dis[v] = dis[u] + Edge[i].cost - mid; 50 if (!vis[v]) 51 { 52 vis[v] = true; 53 q.push(v); 54 cnt[v]++; 55 if (cnt[v] >= n) return false; 56 } 57 } 58 } 59 } 60 return true; 61 } 62 }spfa; 63 int main() 64 { 65 int N,E; 66 while (~scanf("%d%d", &N, &E)) 67 { 68 spfa.Init(); 69 int l=1, r = -1; 70 for (int i = 1; i <= E; i++) 71 { 72 int from, to, cost; 73 scanf("%d%d%d", &from, &to, &cost); 74 spfa.addedge(from, to, cost); 75 r = max(r, cost); 76 } 77 spfa.n = N; 78 if (spfa.cal_spfa(r)) 79 { 80 printf("Infinite\n"); 81 } 82 else if (!spfa.cal_spfa(1)) 83 { 84 printf("No Solution\n"); 85 } 86 else 87 { 88 while (l <= r) 89 { 90 int mid = (l + r) / 2; 91 if (spfa.cal_spfa(mid)) l = mid+1; 92 else r = mid - 1; 93 } 94 printf("%d\n", r); 95 } 96 } 97 return 0; 98 }
6、uvalive 3661 Animal Run
题意:有n*m的方格,有横、竖、主对角线边。一群动物想要从左上角到达右下角。为了防止逃跑,每条道路都需要若干人员堵住。现在需要求最少的人数,使得没有一只动物能够逃离。
思路:最简单的思路:求原图的最小割。但是会超时。因为原图为S-T平面图,转换为其对偶图,求由原图的面所构成的S*到T*的最短路即原图S到T的最小割。用SPFA的话,需要用优先队列优化。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<cstring> 3 #include<queue> 4 #include<cstdio> 5 using namespace std; 6 const int maxe = 1000 * 1000 * 3 * 2 + 100; 7 const int maxn = 1000 * 1000*2 + 100; 8 const int INF = 0x3f3f3f3f; 9 struct edge 10 { 11 int from, to, cost, next; 12 edge(int ff=0,int tt=0,int cc=0,int nn=0):from(ff),to(tt),cost(cc),next(nn){ } 13 }Edge[maxe]; 14 int Head[maxn],totedge; 15 16 struct node 17 { 18 int id, dist; 19 node(int ii=0,int dd=0):id(ii),dist(dd){ } 20 friend bool operator<(const node&a, const node&b) 21 { 22 return a.dist > b.dist; 23 } 24 }; 25 struct SPFA 26 { 27 int st, ed; 28 int dis[maxn]; 29 bool vis[maxn]; 30 void Init() 31 { 32 memset(Head, -1, sizeof(Head)); 33 totedge = 0; 34 } 35 void Set(int source, int dest) 36 { 37 st = source, ed = dest; 38 } 39 void addedge(int from, int to, int cost) 40 { 41 Edge[totedge] = edge(from, to, cost, Head[from]); 42 Head[from] = totedge++; 43 Edge[totedge] = edge(to, from, cost, Head[to]); 44 Head[to] = totedge++; 45 } 46 void Cal_spfa() 47 { 48 memset(dis, INF, sizeof(dis)); 49 memset(vis, 0, sizeof(vis)); 50 dis[st] = 0; 51 priority_queue<node>q; 52 q.push(node(st,0)); 53 while (!q.empty()) 54 { 55 node cur = q.top(); 56 q.pop(); 57 int u = cur.id; 58 if (vis[u]) continue; 59 vis[u] = true; 60 for (int i = Head[u]; i != -1; i = Edge[i].next) 61 { 62 int v = Edge[i].to; 63 if (dis[v] > dis[u] + Edge[i].cost) 64 { 65 dis[v] = dis[u] + Edge[i].cost; 66 q.push(node(v, dis[v])); 67 } 68 } 69 } 70 } 71 }spfa; 72 int main() 73 { 74 int n, m; 75 int Case = 1; 76 while (~scanf("%d%d", &n, &m) && (n + m)) 77 { 78 spfa.Init(); 79 int nums = (n-1)*(m-1) * 2; 80 int source = nums + 1, dest = nums + 2; 81 //行 82 for (int i = 1; i <= n; i++) 83 { 84 for (int j = 1; j <= m - 1; j++) 85 { 86 int cap; 87 scanf("%d", &cap); 88 if (i == 1) 89 { 90 spfa.addedge(j * 2, dest, cap); 91 } 92 else if (i == n) spfa.addedge(source, (i - 2) *(m-1)* 2 + j * 2-1, cap); 93 else 94 { 95 spfa.addedge((i - 2)*(m - 1) * 2 + j * 2 - 1, (i - 1)*(m - 1) * 2 + j * 2, cap); 96 } 97 } 98 } 99 //列 100 for (int i = 1; i <= n - 1; i++) 101 { 102 for (int j = 1; j <= m; j++) 103 { 104 int cap; 105 scanf("%d", &cap); 106 if (j == 1) 107 { 108 spfa.addedge(source, (i - 1)*(m - 1)*2 + 1, cap); 109 } 110 else if (j == m) 111 { 112 spfa.addedge(i*(m-1)*2, dest, cap); 113 } 114 else 115 { 116 spfa.addedge((i - 1)*(m - 1) * 2 + (j - 1) * 2, (i - 1)*(m - 1) * 2 + (j - 1) * 2 + 1, cap); 117 } 118 } 119 } 120 //对角线 121 for (int i = 1; i <= n - 1; i++) 122 { 123 for (int j = 1; j <= m - 1; j++) 124 { 125 int cap; 126 scanf("%d", &cap); 127 spfa.addedge((i - 1)*(m - 1) * 2 + j * 2 - 1,(i - 1)*(m - 1) * 2 + j * 2, cap); 128 } 129 } 130 spfa.Set(source, dest); 131 spfa.Cal_spfa(); 132 printf("Case %d: Minimum = %d\n", Case++, spfa.dis[dest]); 133 } 134 return 0; 135 }
7、uva 10603 Fill
题意:有三个水壶a,b,c,其中a和b是空的,c是满的。每次可以把水从一个壶倒到另一个壶,直到前一个已经倒空或者后一个壶已经倒满。问最少的累计倒水的体积,使得其中某一个壶的水的体积为d?如果达不到d,则输出离d最近的那个目标体积的对应最少的累计倒水的体积。
思路:通过BFS搜索,记录已经访问过的状态,每次更新达到壶中水的体积最少的累计次数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<queue> 3 #include<algorithm> 4 #include<cstring> 5 #include<cstdio> 6 using namespace std; 7 int c[3], d; 8 const int maxv = 210; 9 int dist[maxv];//得到容量为i最少的倒水体积 10 bool vis[maxv][maxv];//表示a、b、c状态为i、j、d-i-j是否访问过 11 struct node 12 { 13 int dis;//从开始到当前已倒过的水的体积 14 int v[3]; 15 node(int dd=0,int vva=0,int vvb=0,int vvc=0):dis(dd) 16 { 17 v[0] = vva, v[1] = vvb, v[2] = vvc; 18 } 19 friend bool operator<(const node&a, const node&b) 20 { 21 return a.dis > b.dis; 22 } 23 }; 24 void BFS() 25 { 26 memset(dist, -1, sizeof(dist)); 27 memset(vis, 0, sizeof(vis)); 28 priority_queue<node>q; 29 q.push(node(0, 0, 0, c[2])); 30 vis[0][0] = true; 31 while (!q.empty()) 32 { 33 node cur = q.top(); 34 q.pop(); 35 for (int i = 0; i < 3; i++) 36 { 37 if (dist[cur.v[i]] == -1 || dist[cur.v[i]] > cur.dis) dist[cur.v[i]] = cur.dis; 38 } 39 for (int i = 0; i < 3; i++) 40 { 41 for (int j = 0; j < 3; j++) 42 { 43 if (i == j || cur.v[i] == 0 || cur.v[j] == c[j]) continue; 44 int add = min(cur.v[i], c[j] - cur.v[j]); 45 node tmp = cur; 46 tmp.v[i] -= add; 47 tmp.v[j] += add; 48 tmp.dis += add; 49 if (!vis[tmp.v[0]][tmp.v[1]])//由于肯定会越倒,dis肯定越大,所以到后面如果出现重复的,则不再添加 50 { 51 vis[tmp.v[0]][tmp.v[1]] = 1; 52 q.push(tmp); 53 } 54 } 55 } 56 } 57 } 58 int main() 59 { 60 int t; 61 scanf("%d", &t); 62 while (t--) 63 { 64 for (int i = 0; i <= 2; i++) scanf("%d", &c[i]); 65 scanf("%d", &d); 66 BFS(); 67 while (d >= 0) 68 { 69 if (dist[d] >= 0) 70 { 71 printf("%d %d\n", dist[d], d); 72 break; 73 } 74 d--; 75 } 76 } 77 return 0; 78 }
8、uva 10269 Adventure of Super Mario
题意:给出无向图,求起点到终点的最小花费。每条路,如果走路,花费时间等于路的长度;如果坐magic boot,可以直达,时间为0.一次坐magic boot,可以经过多条道路,但是所有经过道路总长不超过L,并且途中不能穿过城堡。magic boot最多只能使用K次。
思路:先求出两两可直达的最短距离(即途中不经过城堡)。然后spfa判断dis[i][k](到i处剩余k次时的最小花费)之间的转换,可通过优先队列优化。(原本以为dis一维可以,用队列记录到达的结点和飞行情况,但是解不出来……)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <cmath> 5 #include <cstdlib> 6 #include <queue> 7 using namespace std; 8 9 const int maxn = 155; 10 const int INF = 0x3f3f3f3f; 11 typedef long long ll; 12 int A, B, M, L, K; 13 int mp[maxn][maxn]; 14 15 void floyd() 16 { 17 for (int k = 1; k <= A; k++) 18 { 19 for (int i = 1; i <= A + B; i++) 20 { 21 for (int j = 1; j <= A + B; j++) 22 { 23 if (mp[i][j] > mp[i][k] + mp[k][j]) 24 { 25 mp[i][j] = mp[i][k] + mp[k][j]; 26 } 27 } 28 } 29 } 30 } 31 32 struct node 33 { 34 int u, k, cos;//当前结点、剩余次数、花费 35 node(int uu = 0, int kk = 0, int cc = 0) :u(uu), k(kk), cos(cc) 36 {} 37 bool operator <(const node& a)const 38 { 39 return cos > a.cos; 40 } 41 }; 42 int dis[maxn][15]; 43 int spfa() 44 { 45 priority_queue<node> Q; 46 for (int i = 0; i <= A + B; i++) 47 { 48 for (int j = 0; j <= 12; j++) 49 { 50 dis[i][j] = INF; 51 } 52 } 53 Q.push(node(A + B, K, 0)); 54 dis[A + B][K] = 0; 55 while (!Q.empty()) 56 { 57 int u = Q.top().u; 58 int k = Q.top().k; 59 if (u == 1) return Q.top().cos; 60 Q.pop(); 61 for (int i = 1; i <= A + B; i++) 62 { 63 if (i == u) continue; 64 if (mp[u][i] == INF) continue; 65 if (dis[i][k] > dis[u][k] + mp[u][i]) 66 { 67 dis[i][k] = dis[u][k] + mp[u][i]; 68 Q.push(node(i, k, dis[i][k])); 69 } 70 if (mp[u][i] <= L && dis[i][k - 1] > dis[u][k] && k != 0) 71 {//如果可以花费一次使用次数从u直接到i 72 dis[i][k - 1] = dis[u][k]; 73 Q.push(node(i, k - 1, dis[i][k - 1])); 74 } 75 } 76 } 77 } 78 79 int main() 80 { 81 int T; 82 scanf("%d", &T); 83 while (T--) 84 { 85 scanf("%d %d %d %d %d", &A, &B, &M, &L, &K); 86 memset(mp, INF, sizeof(mp)); 87 for (int i = 1; i <= A + B; i++) 88 { 89 mp[i][i] = 0; 90 } 91 int a, b, c; 92 for (int i = 0; i < M; i++) 93 { 94 scanf("%d %d %d", &a, &b, &c); 95 mp[a][b] = mp[b][a] = c; 96 } 97 floyd(); 98 printf("%d\n", spfa()); 99 } 100 return 0; 101 }
出处:http://www.cnblogs.com/ivan-count/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。