UVA 11478 Halum(差分约束 SPFA判负环)

You are given a directed graph G(V; E) with a set of vertices and edges. Each edge (i; j) that connects
some vertex i to vertex j has an integer cost associated with that edge.
De ne the operation Halum(v; d) to operate on a vertex v using an integer d as follows: subtract
d from the cost of all edges that enter v and add d to the cost of every edge that leaves v.
As an example of that operation, consider graph G that has three vertices named (1, 2, 3) and two
edges. Edge (1, 2) has cost -1, and edge (2,3) has cost 1. The operation Halum(2; 3) operates on
edges entering and leaving vertex 2. Thus, edge (1, 2) gets cost -1-(-3)=2 and the edge (2, 3) gets cost
1 + (-3) = -2.
Your goal is to apply the Halum function to a graph, potentially repeatedly, until every edge in the
graph has at least a certain cost that is greater than zero. You have to maximize this cost.

Input
Two space-separated integers per case: V (V 500) and E (E 2700). E lines follow. Each line
represents a directed edge using three space-separated integers (u; v; d). Absolute value of cost can be
at most 10000.

Output
If the problem is solvable, then print the maximum possible value. If there is no such solution print
No Solution'. If the value can be arbitrary large printInfinite’

Sample Input
2 1
1 2 10
2 1
1 2 -10
3 3
1 2 4
2 3 2
3 1 5
4 5
2 3 4
4 2 5
3 4 2
3 1 0
1 2 -1

Sample Output
Infinite
Infinite
3
1

description:

给定一个有向图,边有权值,每次操作指定一个点u,一个值d,使所有u的出边+d,所有入边-d。问经过数次操作后,最小正边权最大为多少。如可以无限大就输出Infinite,如不能为正就输出No Solution

input

多组数据,输入到EOF
每组数据第一行包含2个整数n(点),m(边)
接下来m行每行包含3个整数u,v,w
表示u到v有一条边权为w的边

output

每组数据一个输出表示答案

思路:
(copy一段)
要求输出最小的最大,考虑二分答案
每次二分一个权值val,check能否找到一个方案使所有边的权值大于val。
考虑任意一个点,他对他所有的入边和出边的贡献是相同的,设为sumi
考虑一条边(a,b),其操作之后的权值为w(a,b)+suma-sumb ,要求权值大于val的,所以:w(a,b)+suma-sumb > val
变形之后得到:sumb-suma <= w(a,b)-val
sumb <= w(a,b)-val + suma
这就是差分约束的典型等式了。
考虑SPFA中处理的等式 dis[v]< w(u,v) + dis[u]。
形式是一样的,这里w(a,b)-val和w(u,v)都是一个常量,要解像这样的这一串不等式就可以用SPFA的方法处理它,SPFA能跑出来的就是一组可行解,suma就是a点的dis值。
如果跑不出来,说明有负环,没有可行解。 (跑最短路只要能跑出来就说明更新条件是满足的,而跑不出来的情况只有出现负环,因为我们求的是最短路,有负环的话就可以一直跑这个负环,无穷无尽…显然是非法的)
那对于sumb <= w(a,b)-val + suma。
我们就连一条a到b,权值为w(a,b)-val的边,SPFA需要一个源点,因为是单源最短路嘛,所以我们就加一个虚拟源点S,使其与所有点连边,边权为0。 (如果保证连通的情况下,而且没有什么要你输出解集的要求,就可以在原图中随便定一个点为源点)。
题目规定了上界,所以只要判一下val为最上界是否满足有解,如果是,就输出Infinite。
题目规定为正数,所以判一下下界1,如果都不行,无解的话就输出No Solution。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;

const int INF = 0x7fffffff;
const int N = 100010;

struct Edge{
    int to, next, w;
}ed[N<<1];

queue <int> state;

int head[N], dis[N], vis[N], times[N];
int n, m, ans, idc=0, S=0, lim=0;

void init(){
    idc=0, ans=0, S=0, lim=0;
    memset(head, 0, sizeof(head));
}

void add(int u, int v, int w){
    ed[++idc].to = v;
    ed[idc].w = w;
    ed[idc].next = head[u];
    head[u] = idc;
}

bool spfa(){
    for(int i=0; i<=n+1; i++) dis[i] = INF;
    memset(times, 0, sizeof(times));
    memset(vis, 0, sizeof(vis));
    while( !state.empty() ) state.pop();
    state.push(S);
    vis[S]=1, dis[S]=0, times[S]=1;
    while( !state.empty() ){
        int u = state.front();
        vis[u] = 0; state.pop();
        for(int i=head[u]; i; i=ed[i].next){
            int v = ed[i].to;
            if(dis[v] > dis[u] + ed[i].w){//如果不满足 sumb <= w(a,b)-val + suma 
                dis[v] = dis[u] + ed[i].w;//就调整 
                if(!vis[v]){
                    vis[v] = 1;
                    state.push(v);
                    times[v]++;//加入队列的次数 
                    if(times[v] > n + 1) return 0;//有负环,方程无解                  
                }
            }
        }
    }
    return 1;
}

bool check(int val){
    bool flag;
    for(int i=1; i<=idc; i++)
        ed[i].w -= val;//sumb <= w(a,b)-val + suma 
    flag = spfa();
    for(int i=1; i<=idc; i++)
        ed[i].w += val;//还原 
    return flag;
}

int main(){
    while(scanf("%d%d", &n, &m) != EOF){
        init();
        for(int i=1; i<=m; i++){
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            add(u, v, w);
            lim = max(lim, w);
        }
        for(int i=1; i<=n; i++)
            add(S, i, 0);
        if( check(lim + 1) ){//可以只增不减达到无穷 
            printf("Infinite\n");
            continue ;
        }
        else if( !check(1) ){//没有'正'解 
            printf("No Solution\n"); 
            continue ;
        }
        int l = 1, r = lim;
        while(l <= r){
            int mid = (l + r) >> 1;
            if( check(mid) ){
                ans = mid;
                l = mid + 1;
            }
            else r = mid - 1;
        }       
        printf("%d\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值