1. 程式人生 > >POJ #1860 Currency Exchange 最短路徑算法 判斷負環

POJ #1860 Currency Exchange 最短路徑算法 判斷負環

短路徑 pos main vector message back show 返回 .cn

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; }
View Code

  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;
}
View Code

  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;
}
View Code

POJ #1860 Currency Exchange 最短路徑算法 判斷負環