Bellman-Ford 代码实现
  anLrwkgbyYZS 2023年12月30日 16 0


目录

1.  算法学习

1.1  存在负环就没有最短路

1.2  Bellman-Ford 的适用情况

1.3  核心算法思想

2.  代码实现

3.  代码检验,poj 1860 Currency Exchange


 

1.  算法学习

1.1  存在负环就没有最短路

首先需要理解"存在负环就没有最短路"原因:你可以一直在这个负环打圈,路径权值会不断减小,不断地趋近于负无穷大,不断的打圈停不下来,就没有最短路。得到结论,存在负环就没有最短路。

1.2  Bellman-Ford 的适用情况

Bellman-Ford ,用于在负权图中判断是否存在负环,没有负环,则可返回某一点(起始点)到其他点的最短路, 时间复杂度:O(nm), n为节点数,m为边数。

1.3  核心算法思想

Bellman-Ford 的核心思想如下:通过 至多 n-1轮松弛操作,得到了初始点到其他点的最短路,就没有负环;如果可以进行 第 n 轮 松弛操作就有负环,不存在最短路。松弛操作,大家可以去阅读代码,然后画个图去模拟一下。

2.  代码实现

// Bellman-Ford 算法代码实现
// 时间复杂度:O(nm) n为节点数,m为边数
// 用来判断是否存在负环,没有负环找到起始点到其他点的最短路
// bellman_ford 的 基本思路如下:
// 首先需要理解"存在负环就没有最短路",原因,你可以一直在这个负环打圈,路径权值会不断减小,不断地趋近于负无穷大。
// 理解了存在负环就没有最短路,那么如果存在最短路,通过 至多 n-1轮松弛操作即可得到初始点到其他点的最短路。
// 如果还可以进行 第 n 轮 松弛,那么存在负权回路。

#include <iostream>
#include <vector>
using namespace std;

const int N = 110;
const int INF = 1000000007;

// dist[i] 起始点到点 i 的最短距离
int dist[N];
// 边,s 边的起点编号,e 边的终点编号,v 边的长度
typedef struct Edge {
    int s, e, v;
}Edge;

vector <Edge> E;

// n 个节点, 起始点为 start
// 函数 return false ;有负权回路;return true: 没有负权回路
bool bellman_ford(int n, int start) {
    // 初始化
    for(int i=1; i<=n; i++) {
        dist[i]=INF;
    }
    dist[start]=0;

    // n-1轮松弛操作
    for(int i=0; i<n; i++) {
        bool flag = false;
        for(int j=0; j<E.size(); j++) {
            int s = E[j].s;
            int e = E[j].e;
            int v = E[j].v;

            if(dist[e] > dist[s]+v) {
                dist[e] = dist[s]+v;
                flag = true;
            }
        }
        if(!flag) return true;
    }

    // 第 n 轮 松弛 
    for(int j=0; j<E.size(); j++) {
        if(dist[E[j].e] > dist[E[j].s]+E[j].v)
            return false;
    }

    return true;
}

void addEdge(Edge edge) {
    E.push_back(edge);
}
int main()
{
    // n 个节点, m 条边
    int n, m;
    cin >> n >> m;
    int s, e, v;
    Edge edge;
    for(int i=1; i<=m; i++) {
        cin >> s >> e >> v;
        edge.s = s;
        edge.e = e;
        edge.v = v;
        addEdge(edge);
    }
    int start = 1;
    if(!bellman_ford(n, start)) {
        cout << "有负权回路" << endl;
    }
    else {
        cout << "没有负权回路" << endl;
    }
    return 0;
}

/* Test:
3 3
1 2 1
2 3 2
3 1 -2

3 3
1 2 1
2 3 2
3 1 -3

3 3
1 2 1
2 3 2
3 1 -4
*/

3.  代码检验,poj 1860 Currency Exchange

题目传送门:http://poj.org/problem?id=1860,这是一道求正环的题,反着使用 Bellman-Ford 算法即可。

Currency Exchange

Time Limit: 1000MS

 

Memory Limit: 30000K

Total Submissions: 39475

 

Accepted: 15162

Description

Several currency exchange points are working in our city. Let us suppose that each point specializes in two particular currencies and performs exchange operations only with these currencies. There can be several points specializing in the same pair of currencies. Each point has its own exchange rates, exchange rate of A to B is the quantity of B you get for 1A. Also each exchange point has some commission, the sum you have to pay for your exchange operation. Commission is always collected in source currency. 
For example, if you want to exchange 100 US Dollars into Russian Rubles at the exchange point, where the exchange rate is 29.75, and the commission is 0.39 you will get (100 - 0.39) * 29.75 = 2963.3975RUR. 
You surely know that there are N different currencies you can deal with in our city. Let us assign unique integer number from 1 to N to each currency. Then each exchange point can be described with 6 numbers: integer A and B - numbers of currencies it exchanges, and real RAB, CAB, RBA and CBA - exchange rates and commissions when exchanging A to B and B to A respectively. 
Nick has some money in currency S and wonders if he can somehow, after some exchange operations, increase his capital. Of course, he wants to have his money in currency S in the end. Help him to answer this difficult question. Nick must always have non-negative sum of money while making his operations. 

Input

The first line of the input contains four numbers: N - the number of currencies, M - the number of exchange points, S - the number of currency Nick has and V - the quantity of currency units he has. The following M lines contain 6 numbers each - the description of the corresponding exchange point - in specified above order. Numbers are separated by one or more spaces. 1<=S<=N<=100, 1<=M<=100, V is real number, 0<=V<=103. 
For each point exchange rates and commissions are real, given with at most two digits after the decimal point, 10-2<=rate<=102, 0<=commission<=102. 
Let us call some sequence of the exchange operations simple if no exchange point is used more than once in this sequence. You may assume that ratio of the numeric values of the sums at the end and at the beginning of any simple sequence of the exchange operations will be less than 104. 

Output

If Nick can increase his wealth, output YES, in other case output NO to the output file.

Sample Input

3 2 1 20.0
1 2 1.00 1.00 1.00 1.00
2 3 1.10 1.00 1.10 1.00

Sample Output

YES

参考代码:

#include <iostream>
#include <vector>
using namespace std;

const int MAXN = 110;
const int INF = 1000000007;

// 原货币换成第 i 种货币
double dist[MAXN];

// s 起点编号,e 终点编号
// rate 起点到终点的汇率,起点到终点所需的手续费
typedef struct edge {
    int s, e;
    double rate, commission;
}Edge;

vector <Edge> E;

// n 个节点, 起始点为 start
bool bellman_ford(int n, int start,double V) {
    // 初始化
    for(int i=1; i<=n; i++) {
        dist[i] = 0;
    }
    dist[start] = V;

    for(int i=1; i<n; i++) {
        bool flag = false;
        for(int j=0; j<E.size(); j++) {
            int s = E[j].s;
            int e = E[j].e;
            double commission = E[j].commission;
            double rate = E[j].rate;
            if(dist[e] < (dist[s]-commission)*rate) {
                flag = true;
                dist[e] = (dist[s]-commission)*rate;
            }
        }
        if(!flag) return false; // 没有正环
    }
    for(int j=0; j<E.size(); j++) {
        int s = E[j].s;
        int e = E[j].e;
        double commission = E[j].commission;
        double rate = E[j].rate;
        if(dist[e] < (dist[s]-commission)*rate)
            return true; // 有正环
    }
    return false; // 没有正环
}

// 增加一条边
void AddEdge(int a, int b, double rab, double cab) {
    Edge edge;
    edge.s = a;
    edge.e = b;
    edge.rate = rab;
    edge.commission = cab;
    E.push_back(edge);

}

int main() {
    // N 货币数量, M 交换点数量
    int N, M, S;
    double V;
    while(cin >> N >> M >> S >> V) {
        int a, b;
        double rateAToB, commissionAToB, rateBToA, commissionBtoA;
        E.clear();
        for(int i=1; i<=M; i++) {
                cin >> a >> b >> rateAToB >> commissionAToB >> rateBToA >> commissionBtoA;
                AddEdge(a, b, rateAToB, commissionAToB);
                AddEdge(b, a, rateBToA, commissionBtoA);
        }

        if(bellman_ford(N, S, V)) cout << "YES" << endl;
        else cout << "NO" << endl;
    }
    return 0;
}

 

 

 

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年12月30日 0

暂无评论

anLrwkgbyYZS