例题5.16 Halum操作 UVa11478

该博客通过详细分析介绍了如何利用BellmanFord算法和二分法解决UVa11478题目的Halum操作问题,通过将问题转化为差分约束系统,然后通过构建特定的边来判断解的存在性,最终使用BellmanFord算法判断负环,以确定最小非负边权的最大值。
摘要由CSDN通过智能技术生成

1.题目描述:点击打开链接

2.解题思路:本题利用BellmanFord算法+二分解决。本题要求执行完一系列Halum操作后,可以让边权的最小值非负且尽量大。自然想到可以用二分法来解决。假设答案是x。即问题转化为所有边的边权经过操作后都大于等于x。然而这里有一个问题,我们根本不知道应该怎么选取相应的v,d,使得可以达到这个目标。但是仔细观察后就可以注意到一个特点:不同的操作影响是相互独立的,即影响是符合叠加原理的。假设我们对u结点执行了d1,d2..dk种操作,可以等价为只执行了d1+d2+..+dk这一种操作。这样,我们可以考虑把每个结点i等效的操作数设置为Xi,那么对于每一条边A->B,有w(A,B)+X(A)-X(B)>=x,移项后得到X(B)-X(A)<=w(A,B)-x。这样,我们实际上得到了m个不等式组。这个不等式组构成了一个“差分约束系统”。如果这个不等式组有解,那么x值就是可以得到的,否则就是不可能得到的。那么现在问题转化为如何判断解是否存在。

考虑到最短路中的不等式d[v]<=d[u]+w[u,v],我们可以用最短路来判断,把0结点处的X(0)设置为0,如果有X(B)-X(A)<=w(A,B)-x,就连一条A->B的边,边权恰好是w(A,B)-x。这样就和最短路的不等式完全一样了。可以直接用Dijkstra算法求解了。但是我们没有必要真的求出来每个点的具体值,而只是判断解是否存在。最短路不存在的条件当且仅当图中存在负圈。那么就可以用BellmanFord算法来判断解的存在性了。至此,问题得以解决。

3.代码:

#include<iostream>
#include<algorithm>
#include<cassert>
#include<string>
#include<sstream>
#include<set>
#include<bitset>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cctype>
#include<list>
#include<complex>
#include<functional>
using namespace std;

#define me(s)  memset(s,0,sizeof(s))
#define rep(i,n) for(int i=0;i<(n);i++)
#define pb push_back
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
typedef pair <int,int> P;

const int INF=1e9;
const int maxn=500+10;
const int maxm=2700+10;

struct Edge{int to,dist;};

struct BellmanFord
{
    int n,m;
    Edge edges[maxm];
    int head[maxn];
    int next[maxm];
    bool inq[maxn];
    int d[maxn],cnt[maxn];
    void init(int n)
    {
        this->n=n;
        m=0;
        memset(head,-1,sizeof(head));
    }
    void addedge(int u,int v,int dist)
    {
        edges[m]=Edge{v,dist};
        next[m]=head[u];
        head[u]=m++;
    }

    bool negativeCycle()
    {
        queue<int>q;
        memset(inq,0,sizeof(inq));
        memset(cnt,0,sizeof(cnt));
        for(int i=0;i<n;i++){d[i]=0;q.push(i);}
        while(!q.empty())
        {
            int u=q.front();q.pop();
            inq[u]=false;
            for(int i=head[u];~i;i=next[i])
            {
                Edge&e=edges[i];
                if(d[e.to]>d[u]+e.dist)
                {
                    d[e.to]=d[u]+e.dist;
                    if(!inq[e.to])
                    {
                        q.push(e.to);inq[e.to]=true;
                        if(++cnt[e.to]>n)return true;
                    }
                }
            }
        }
        return false;
    }
};

BellmanFord solver;

bool test(int x)//判断边权的最小值是否可以为x,等价于判断所有边权都减掉x后,整个图是否存在负圈
{
    for(int i=0;i<solver.m;i++)
        solver.edges[i].dist-=x;
    bool ret=solver.negativeCycle();
    for(int i=0;i<solver.m;i++)
        solver.edges[i].dist+=x;
    return !ret;
}

int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        solver.init(n);
        int ub=0;
        while(m--)
        {
            int u,v,d;
            scanf("%d%d%d",&u,&v,&d);
            ub=max(ub,d);
            solver.addedge(u-1,v-1,d);
        }
        if(test(ub+1))puts("Infinite"); //如果最小值可以达到最大值+1,说明是可以无限大的,因为如果反复执行相同的操作,就会让边权继续增大,趋于无限
        else if(!test(1))puts("No Solution");//如果最小值连1都达不到,说明无解
        else
        {
            int L=2,R=ub,ans=1;
            while(L<=R)
            {
                int M=L+(R-L)/2;
                if(test(M)){ans=M;L=M+1;}
                else R=M-1;
            }
            printf("%d\n",ans);
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值