Description
题目描述在这里:链接
更多的样例:链接
思路
我们把每种货币看成图的顶点,而每个交换站点实现一对货币的交换,可认为增加一个交换站点就是增加两个顶点间的一个环路。从样例中可以知道,如果想要让NICK的资金增加,那么就需要存在一个权值为正的环路,使得货币总价值能够无限上升。
所以现在的问题变成了如何判断图中是否有正环。由于货币交换后总价值可能降低,也就是说可能存在负权边,那么 Dijkstra 就不适用了,应该采用能判断负环的 Bellman_ford ,还有它的优化算法 SPFA 。由于需要判断图中是否存在能使货币总价值无限上升的正环,那么松弛边的代码也就不是原来求最短路的松弛操作了,需要变成了 dis[v] < (dis[u] - C) * R 。改了松弛操作,原本判断负环的操作也就对应的变成了判断正环。
AC 代码如下:
1. BFS 判断货币交换后是否增值。依据:货币交换相当于增加环路,那么从 S 出发就总能回到 S,通过 S 的值来判断其是否增值。
#include<iostream> #include<algorithm> #include<queue> #include<vector> #include<cstring> using namespace std; const int INF = 0x3f3f3f3f; const int MAXN = 105; int N, M; double V; struct Edge{ int to; double R, C; }; vector<Edge> G[MAXN]; //G[i]代表从i出发的边,vector存储边 void addEdge (int u, int v, double r, double c) { Edge tmp; tmp.to = v; tmp.R = r; tmp.C = c; G[u].push_back(tmp); } double dis[MAXN]; bool inQueue[MAXN]; //spfa算法判断dis[s]是否可能大于V (实质是BFS) bool spfa (int s) { for (int i = 1; i <= N; i++) {dis[i] = 0.0; inQueue[i] = false; } dis[s] = V; queue<int> q; q.push(s); inQueue[s] = true; while (!q.empty()) { int u = q.front(); q.pop(); inQueue[u] = false; for (int j = 0; j < G[u].size(); j++) { int v = G[u][j].to; double r = G[u][j].R, c = G[u][j].C; if (dis[v] < (dis[u] - c)*r ) { dis[v] = (dis[u] - c)*r; if (!inQueue[v]) { q.push (v); inQueue[v] = true; } } } if (dis[s] > V) { return true; } } return false; } int main(void) { int S; while (cin >> N >> M >> S >> V) { for (int i = 1; i <= N; i++) G[i].clear(); for (int i = 1; i <= M; i++) { int u, v; double R1, C1, R2, C2; cin >> u >> v >> R1 >> C1 >> R2 >> C2; addEdge(u, v, R1, C1); addEdge(v, u, R2, C2); } //spfa算法判断dis[s]是否可能大于V if (spfa(S)) cout << "YES" << endl; else cout << "NO" << endl; } return 0; }
2.Bellman_ford 判断是否存在正环。
#include<iostream> #include<algorithm> #include<queue> #include<vector> #include<cstring> using namespace std; const int INF = 0x3f3f3f3f; const int MAXN = 105; int N, M; double V; struct Edge{ int to; double R, C; Edge(int v, double r, double c) : to(v), R(r), C(c) {} }; vector<Edge> G[MAXN]; //G[i]代表从i出发的边,vector存储边 void addEdge (int u, int v, double r, double c) { //Edge tmp; tmp.to = v; tmp.R = r; tmp.C = c; //G[u].push_back(tmp); G[u].push_back(Edge(v, r, c)); } double dis[MAXN]; bool bellman_ford (int s) { for (int i = 1; i <= N; i++) dis[i] = 0.0; dis[s] = V; bool relaxed = false; //哨兵 //最多更新N-1次dis数组 for (int i = 1; i <= N-1; i++) { relaxed = false; //遍历M条边 for (int u = 1; u <= N; u++) { for (int j = 0; j < G[u].size(); j++) { int v = G[u][j].to; double r = G[u][j].R, c = G[u][j].C; if (dis[v] < (dis[u] - c)*r ) { dis[v] = (dis[u] - c)*r; relaxed = true; } } } if (!relaxed) break; } //判断是否存在正环 for (int u = 1; u <= N; u++) { for (int j = 0; j < G[u].size(); j++) { int v = G[u][j].to; double r = G[u][j].R, c = G[u][j].C; //如果还有边可以松弛,说明存在正环 if (dis[v] < (dis[u] - c)*r ) { return false; } } } return true; //返回true说明图不包含正环 //或者用spfa算法直接判断dis[s]是否大于V } int main(void) { int S; while (cin >> N >> M >> S >> V) { for (int i = 1; i <= N; i++) G[i].clear(); for (int i = 1; i <= M; i++) { int u, v; double R1, C1, R2, C2; cin >> u >> v >> R1 >> C1 >> R2 >> C2; addEdge(u, v, R1, C1); addEdge(v, u, R2, C2); } //bellman_ford算法判断是否存在正环 if (!bellman_ford(S)) cout << "YES" << endl; else cout << "NO" << endl; } return 0; }
3.SPFA 判断是否存在正环。 SPFA算法的模板及分析在我的另一篇博客里:链接
#include<iostream> #include<algorithm> #include<queue> #include<vector> #include<cstring> using namespace std; const int INF = 0x3f3f3f3f; const int MAXN = 105; int N, M; double V; struct Edge{ int to; double R, C; }; vector<Edge> G[MAXN]; //G[i]代表从i出发的边,vector存储边 void addEdge (int u, int v, double r, double c) { Edge tmp; tmp.to = v; tmp.R = r; tmp.C = c; G[u].push_back(tmp); } double dis[MAXN]; bool inQueue[MAXN]; int cnt[MAXN]; //spfa判断是否存在正环 bool spfa (int s) { memset (cnt, 0, sizeof(cnt)); memset (dis, 0.0, sizeof(dis)); memset (inQueue, false, sizeof(inQueue)); dis[s] = V; queue<int> q; q.push(s); inQueue[s] = true; while (!q.empty()) { int u = q.front(); q.pop(); inQueue[u] = false; for (int j = 0; j < G[u].size(); j++) { int v = G[u][j].to; double r = G[u][j].R, c = G[u][j].C; if (dis[v] < (dis[u] - c)*r ) { dis[v] = (dis[u] - c)*r; if (!inQueue[v]) { q.push (v); inQueue[v] = true; if (++cnt[v] > N) return false; } } } } return true; //返回true说明图中不存在正环 } int main(void) { int S; while (cin >> N >> M >> S >> V) { for (int i = 1; i <= N; i++) G[i].clear(); for (int i = 1; i <= M; i++) { int u, v; double R1, C1, R2, C2; cin >> u >> v >> R1 >> C1 >> R2 >> C2; addEdge(u, v, R1, C1); addEdge(v, u, R2, C2); } //spfa算法判断是否存在正环 if (!spfa(S)) cout << "YES" << endl; else cout << "NO" << endl; } return 0; }