POJ 3259 Wormholes(Floyd/Bellman_ford/SPFA 判负环)

题目链接:http://poj.org/problem?id=3259

解题思路:只要图中存在某个圈,沿着这个圈走一圈之后时间能够回退(即负环),那么一定可以完成题目所说之事。


早期floyd做法

做法判断是否有节点到自身的最短路小于0

1829ms

///*** Floyed 法求任意两点最短路
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

int dp[505][505],n;

void solve()
{
    for (int k=1;k<=n;k++)
        for (int i=1;i<=n;i++){
            for (int j=1;j<=n;j++)
                if (dp[i][j]>dp[i][k]+dp[k][j])
                    dp[i][j]=dp[i][k]+dp[k][j];
            if (dp[i][i]<0) { cout<<"YES"<<endl; return ;}
        }
    cout<<"NO"<<endl;
}

int main()
{
    std::ios::sync_with_stdio(false);
    int t,m,s;
    cin>>t;
    while (t--){
        cin>>n>>m>>s;
        memset(dp,0x3f3f3f3f,sizeof(dp));
        for (int i=1;i<=n;i++) dp[i][i]=0;

        while (m--){
            int u,v,w;
            cin>>u>>v>>w;
            if (w<dp[u][v])dp[u][v]=dp[v][u]=w;
        }
        while (s--){
            int u,v,w;
            cin>>u>>v>>w;
            dp[u][v]=-w;
        }

        solve();
    }
    return 0;
}

Bellman_ford 做法

节点数为V,假设第V次更新数组仍然可以缩小,证明有负环

329ms

#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<set>
#include<map>
#include<algorithm>

using namespace std;

#define ll long long
#define ull unsigned long long
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define for0(i,a,b) for (int i=a;i<b;i++)
#define rof1(i,a,b) for (int i=a;i>=b;i--)
#define rof0(i,a,b) for (int i=a;i>b;i--)
#define pb push_back
#define fi first
#define se second
#define debug(x) printf("----Line %s----\n",#x)
#define pt(x,y) printf("%s = %d\n",#x,y)
#define INF 0x3f3f3f3f
#define dfl(x) ll x;scanf("%I64d",&x)
#define df2l(x,y) ll x,y;scanf("%I64d %I64d",&x,&y)
#define df(x) int x;scanf("%d",&x);
#define df2(x,y) int x,y;scanf("%d %d",&x,&y)
#define mod 1000000007
#define duozu(T) int T;scanf("%d",&T);while (T--)

const int N = 500;

struct Edge
{
    int to,last,w;
}edge[2500*2+200+5];

int id,head[505];
void add(int u,int v,int w){edge[id].to = v;edge[id].w = w;edge[id].last = head[u];head[u] = id++;}

int dis[N];

bool Bellman_ford(int V)
{
    memset(dis,INF,sizeof dis);
    dis[1] = 0;
    for1(k,1,V){
        for1(u,1,V){
            for (int j=head[u];j!=0;j=edge[j].last){
                int v = edge[j].to;
                if (dis[v]>dis[u]+edge[j].w){
                    dis[v] = dis[u]+edge[j].w;
                    if (k==V) return true;
                }
            }
        }
    }
    return false;
}

int main()
{
    //freopen("C:/Users/DELL/Desktop/input.txt", "r", stdin);
    //freopen("C:/Users/DELL/Desktop/output.txt", "w", stdout);
    duozu(T){
        id = 1;
        memset(head,0,sizeof head);
        int n,m,mm;
        scanf("%d %d %d",&n,&m,&mm);
        int u,v,w;
        for0(i,0,m){
            scanf("%d %d %d",&u,&v,&w);
            add(u,v,w);
            add(v,u,w);
        }
        for0(i,0,mm){
            scanf("%d %d %d",&u,&v,&w);
            add(u,v,-w);
        }
        bool flag = Bellman_ford(n);
        printf("%s\n",flag?"YES":"NO");
    }
    return 0;
}

SPFA做法(队列优化的bellman_ford算法)

优化原因:只将上一轮松弛过的点入队列,没松弛过的点不会对其他点更新就不用进队列了

思路同bellman,一个点被修改V次,证明有负环

94ms

#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<set>
#include<map>
#include<algorithm>

using namespace std;

#define ll long long
#define ull unsigned long long
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define for0(i,a,b) for (int i=a;i<b;i++)
#define rof1(i,a,b) for (int i=a;i>=b;i--)
#define rof0(i,a,b) for (int i=a;i>b;i--)
#define pb push_back
#define fi first
#define se second
#define debug(x) printf("----Line %s----\n",#x)
#define pt(x,y) printf("%s = %d\n",#x,y)
#define INF 0x3f3f3f3f
#define dfl(x) ll x;scanf("%I64d",&x)
#define df2l(x,y) ll x,y;scanf("%I64d %I64d",&x,&y)
#define df(x) int x;scanf("%d",&x);
#define df2(x,y) int x,y;scanf("%d %d",&x,&y)
#define mod 1000000007
#define duozu(T) int T;scanf("%d",&T);while (T--)

const int N = 500+5;
const int M = 5200+5;

struct Edge
{
    int to,last,w;
}edge[M];

int id,head[N];

void add(int u,int v,int w)
{
    edge[id].to = v;
    edge[id].w = w;
    edge[id].last = head[u];
    head[u] = id++;
}

void init()
{
    id = 1;
    memset(head,0,sizeof head);
}

int dis[N];
int val[N];

bool SPFA(int V)//V表示节点数
{
    memset(dis,INF,sizeof dis);
    memset(val,0,sizeof val);

    int root = 1;
    bool flag = false;
    dis[root] = 0;
    queue<int>que;
    que.push(1);

    while (!que.empty()){
        int now = que.front();que.pop();
        int v;
        for (int i=head[now];i!=0;i=edge[i].last){
            v = edge[i].to;
            if (dis[v]>dis[now]+edge[i].w){
                dis[v] = dis[now] + edge[i].w;
                que.push(v);
                val[v]++;
                if (val[v]==V) {flag = true;break;}
            }
        }
        if (flag) break;
    }
    return flag;
}

int main()
{
    duozu(T){
        int n,m,mm;
        init();
        scanf("%d %d %d",&n,&m,&mm);
        int u,v,w;
        for0(i,0,m){
            scanf("%d %d %d",&u,&v,&w);
            add(u,v,w);
            add(v,u,w);
        }
        for0(i,0,mm){
            scanf("%d %d %d",&u,&v,&w);
            add(u,v,-w);
        }
        bool flag = SPFA(n);
        printf("%s\n",flag?"YES":"NO");
    }
    return 0;
}

SPFA优化

优化原因:被松弛后较小的点对其他点松弛的影响更大,先用这些点更新可以使得进入队列的元素更少

判负环思路同上

313ms(...C++deque太慢了,后来手动模拟了一下deque到了100ms左右,可能本题节点数太少优化不明显)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<set>
#include<map>
#include<algorithm>

using namespace std;

#define ll long long
#define ull unsigned long long
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define for0(i,a,b) for (int i=a;i<b;i++)
#define rof1(i,a,b) for (int i=a;i>=b;i--)
#define rof0(i,a,b) for (int i=a;i>b;i--)
#define pb push_back
#define pf push_front
#define fi first
#define se second
#define debug(x) printf("----Line %s----\n",#x)
#define pt(x,y) printf("%s = %d\n",#x,y)
#define INF 0x3f3f3f3f
#define dfl(x) ll x;scanf("%I64d",&x)
#define df2l(x,y) ll x,y;scanf("%I64d %I64d",&x,&y)
#define df(x) int x;scanf("%d",&x);
#define df2(x,y) int x,y;scanf("%d %d",&x,&y)
#define mod 1000000007
#define duozu(T) int T;scanf("%d",&T);while (T--)

const int N = 500+5;
const int M = 5200+5;

struct Edge
{
    int to,last,w;
}edge[M];

int id,head[N];

void add(int u,int v,int w)
{
    edge[id].to = v;
    edge[id].w = w;
    edge[id].last = head[u];
    head[u] = id++;
}

void init()
{
    id = 1;
    memset(head,0,sizeof head);
}

int dis[N];
int val[N];

bool SPFA(int V)//V表示节点数
{
    memset(dis,INF,sizeof dis);
    memset(val,0,sizeof val);

    int root = 1;
    bool flag = false;
    dis[root] = 0;
    deque<int>que;
    que.pb(1);

    while (!que.empty()){
        int now = que.front();que.pop_front();
        int v;
        for (int i=head[now];i!=0;i=edge[i].last){
            v = edge[i].to;
            if (dis[v]>dis[now]+edge[i].w){
                dis[v] = dis[now] + edge[i].w;
                que.pb(v);
                if (dis[que.front()]<dis[que.back()]){que.pf(v);que.pop_back();}
                val[v]++;
                if (val[v]==V) {flag = true;break;}
            }
        }
        if (flag) break;
    }
    return flag;
}

int main()
{
    duozu(T){
        int n,m,mm;
        init();
        scanf("%d %d %d",&n,&m,&mm);
        int u,v,w;
        for0(i,0,m){
            scanf("%d %d %d",&u,&v,&w);
            add(u,v,w);
            add(v,u,w);
        }
        for0(i,0,mm){
            scanf("%d %d %d",&u,&v,&w);
            add(u,v,-w);
        }
        bool flag = SPFA(n);
        printf("%s\n",flag?"YES":"NO");
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值