poj 1860 (bellman-ford)

题意是:有n种货币(n个节点),2*m种兑换方式(2*m条边),初始值是持有价值为v的某种货币s。问经任意的兑换之后再回到货币s,是否可能使持有价值增加。其中兑换方式是这样的:若货币A和B之间能兑换,则会同时给出Rab,Cab,Rba,Cba,从A到B兑换:VB = (VA-Cab)*Rab,从B到A兑换:VA=(VB-Cba)*Rba。

输入先是一行4个数:n,m,s,v。

然后是m行,每行6个数:A,B,Rab,Cab,Rba,Cba。

想了很久才想明白这个题为什么和bellman-ford完全相同。根据题意,图中有n个点,有2*m条边,每两点之间,要么没有直接的边相连,要么是双向直接相连,这是题目的一点限制,不过不影响算法。这个人手中的货币价值,就是bellman-ford中的节点到原点的距离,就是最终要最大化或最小化的量。只是现在这个“距离”不是简单的把各边权值加起来,而是稍复杂一点的加减乘除运算。bellman-ford的思想是所用边数的budget从0到n-1循环,每个循环里对每个顶点求min(dist[v])。它的“松弛“技术似乎更好理解一些,松弛技术是在每个循环里对每条边进行松弛,看这条边的dst顶点是否要update dist。那么现在几乎在每一点都找到了与bellman-ford对应之处,唯一不同就是给了上个循环的dist,如何求本次dist,这只需要把加法换成题目所给的公式即可。

其实bellman-ford里的负圈,也不仅限于所有边权重加起来是负数的圈。从某点开始,经过每条边自己规定的运算,走一圈又回到这个点,发现要优化的值向我们想要的方向发展了,就说明这是个负圈。比如要求最大值,走一圈发现我们的target变大了,那么总可以不断地走这个圈是其不断变大,这就是负圈了。考察是否有负圈,只需在第n个循环看是否还有update,如果有,说明有负圈。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 210;
float d[MAXN];
float w[MAXN][2];
int u[MAXN];
int v[MAXN];
int main()
{
    int n, m, s;
    float y;
    scanf("%d%d%d%f", &n, &m, &s, &y);
    int len = 0;
    for (int t = 0; t < m; ++t)
    {
        int a,b;
        scanf("%d%d%f%f%f%f", &a, &b, &w[len][0], &w[len][1], &w[len+1][0], &w[len+1][1]);
        u[len] = a;
        v[len] = b;
        u[len+1] = b;
        v[len+1] = a;
        len+=2;
    }
    for (int k = 0; k < MAXN; ++k) d[k] = 0;
    d[s] = y;
    for (int k = 0; k <= n; ++k)
    {
        bool update = false;
        for (int i = 0; i < 2*m; ++i)
        {
            if (d[v[i]] < (d[u[i]]-w[i][1])*w[i][0])
            {
                update = true;
                d[v[i]] = (d[u[i]]-w[i][1])*w[i][0];
                if (k == n) 
                {
                    printf("YES\n"); return 0;
                }
            }
        }
        if (!update) break;
    }
    printf("NO\n");
    return 0;
}

    原文作者:Bellman - ford算法
    原文地址: https://blog.csdn.net/yqfan10/article/details/39246483
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞