例题5.16 Halum操作 UVa11478

来源:互联网 发布:仿手写软件app 编辑:程序博客网 时间:2024/06/09 14:28

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_backtypedef 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);        }    }}


0 0
原创粉丝点击