码迷,mamicode.com
首页 > 编程语言 > 详细

POJ #1860 Currency Exchange 最短路径算法 判断负环

时间:2018-02-15 22:12:41      阅读:259      评论:0      收藏:0      [点我收藏+]

标签:play   jks   存储   space   nbsp   包含   blank   ref   cst   

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 最短路径算法 判断负环

标签:play   jks   存储   space   nbsp   包含   blank   ref   cst   

原文地址:https://www.cnblogs.com/Bw98blogs/p/8449837.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!