EOJ 1270 spfa/floyd

本文探讨了如何通过修改最短路径算法来检测汇率兑换中的潜在漏洞。利用SPFA和Floyd算法处理负权重边的情况,实现了从任意起点出发找到能够获利的货币循环。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意不再赘述。首先建图。我们将一个点周围的点都给兑换成这个点,然后选择最大的值作为这个点的价值,然后这样不断的计算下去,直到计算到初始点,如果我们重新计算的价值大于初始点的值,就表明有兑换漏洞。这个过程看起来是不是很像求最短路呢?只不过我们把更新公式d[v] = min(d[v], d[u] + w[u][v])换成了d[v] = max(d[v], d[u]*w[u][v]),并且d[v]的含义也发生了变化,原来的d表示从初始点到v的最短距离,现在的d[v]表示从价值为1的初始点开始兑换,兑换到v这个点我们能够获得的最大价值。思路相似,我们套用最短路模板,但是我们求得并不是最短路。因为汇率可能小于1,即权值为负。不能用dijkstra。我们使用spfa或者floyd。

具体思路就是从源点src开始套用最短路模板不断的更新,更新结束后我们看一下d[src][src](从价值为1的初始点开始兑换,经历一系列过程再次兑换成初始点所能获得的最大价值)是不是大于初始值,如果大于,就输出yes。

因为对每个点都要spfa,所以我们也可以选择floyd。状态转移公式也变成了d[i][j] = max(d[i][j], d[i][k] * d[k][j])(注意d[i][j]是我们假设初始值是1,得到的答案。如果初始值为a,就要相应的乘以a倍。这也是状态转移公式用乘号的原因)。

spfa代码:

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <queue>
#include <map>
 
using namespace std;
 
const int maxn = 1005;
double G[maxn][maxn];
int n, m;
 
bool spfa(int src){
    int Inqueue[maxn];
    double d[maxn];
    memset(Inqueue, 0, sizeof(Inqueue));
    memset(d, 0, sizeof(d));
    queue<int> Q;
    Q.push(src);
    Inqueue[src] = 1;
    d[src] = 1.0;
    while (!Q.empty()){
        int u = Q.front();
        Q.pop();
        Inqueue[u] = 0;
        for (int v = 1; v <= n; v++){
            if (G[u][v]){
                double cost = G[u][v];
                if (d[v] < d[u] * cost){
                    d[v] = d[u] * cost;
                    if (!Inqueue[v]){
                        Q.push(v);
                        Inqueue[v] = 1;
                    }
                    if (d[src] > 1.0) return true;
                }
            }
        }
    }
    return false;
}
 
int main()
{
    //freopen("1.txt", "r", stdin);
    int Case = 1;
    while (~scanf("%d", &n) && n){
        map<string, int> msi;
        char name[50];
        for (int i = 1; i <= n; i++){
            scanf("%s", name);
            msi[name] = i;
        }
        scanf("%d", &m);
        char u[50], v[50];
        double cost;
        memset(G, 0, sizeof(G));
        for (int i = 0; i < m; i++){
            scanf("%s%lf%s", u, &cost, v);
            int uu = msi[u], vv = msi[v];
            G[uu][vv] = cost;
        }
 
        printf("Case %d: ", Case++);
 
        int ok = 0;
        for (int i = 1; i <= n; i++){
            if (spfa(i)){
                ok = 1;
                break;
            }
        }
        if (ok) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}

floyd代码:

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <queue>
#include <map>
 
using namespace std;
 
const int maxn = 1005;
int n, m;
double d[maxn][maxn];
 
void Init(){
    for (int i = 1; i <= n; i++)
    for (int j = 1; j <= n; j++){
        if (i == j) d[i][j] = 1;
        else d[i][j] = 0;
    }
}
 
void floyd(){
    for (int k = 1; k <= n; k++)
    for (int i = 1; i <= n; i++)
    for (int j = 1; j <= n; j++){
        if (d[i][j] < d[i][k] * d[k][j])
           d[i][j] = d[i][k] * d[k][j];
    }
}
 
int main()
{
    //freopen("1.txt", "r", stdin);
    int Case = 1;
    while (~scanf("%d", &n) && n){
        map<string, int> msi;
        char name[50];
        for (int i = 1; i <= n; i++){
            scanf("%s", name);
            msi[name] = i;
        }
        scanf("%d", &m);
        Init();
        char u[50], v[50];
        double cost;
        for (int i = 1; i <= m; i++){
            scanf("%s%lf%s", u, &cost, v);
            int uu = msi[u], vv = msi[v];
            d[uu][vv] = cost;
        }
 
        floyd();
 
        printf("Case %d: ", Case++);
        int i;
        for (i = 1; i <= n; i++){
            if (d[i][i] > 1){
                printf("Yes\n");
                break;
            }
        }
        if (i == n + 1) printf("No\n");
    }
    return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值