uva 11478 Halum

差分约束,算法导论上有一节介绍的很详细。

下面来说一下这题是怎样转化成差分约束的。

题目要求每条边权值为正,并且是最小边权最大。一般这种求最小值最大的题目都是用二分。我们二分最小权值x,对每个点所有的操作记为sum()。那么所有操作结束后我们可以得到对于一条边 a->b 有w(a,b) + sum(a) - sum(b)  >=  x,移向得 sum(b) - sum(a)  <= w(a,b)  - x。

对于最短路我们有三角不等式:d[b] <= d[a] + w(a,b),移向后  d[b] - d[a] <=  w(a,b)。这和上面的式子很像,所以我们可以用最短路来求解差分约束。如果图中不存在负圈则差分约束存在可行解(这个算法导论上有证明,反证法)。我们设一个虚节点0,并从虚节点到其他每一个节点连一条边(权值可以是任意的,因为每个可行解是可以差一个常数的),然后求出每个点关于虚节点的最短路即可。

#include <iostream>
#include<vector>
#include<cstring>
#include<stdio.h>
#include<queue>
#define maxn 510
#define INF 100000
using namespace std;
int n,m;
struct Edge
{
    int from,to,dist,next;
};
Edge edges[INF];
int head[maxn];
int vis[maxn];
bool inq[maxn];
int d[maxn];
int p[maxn];
int cnt[maxn];
int ec;

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

void add_edge(int from,int to,int dist)
{
    edges[ec].dist = dist;
    edges[ec].from = from;
    edges[ec].to = to;
    edges[ec].next = head[from];
    head[from] = ec;
    ec++;
}

bool negative(int mid)
{
    queue<int>Q;
    memset(inq,0,sizeof inq);
    memset(cnt,0,sizeof cnt);
    for(int i = 0; i <= n; i++)
    {
        d[i] = INF;
        inq[i] = 1;
        Q.push(i);
    }
    d[0] = 0;
    while(!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        inq[u] = 0;
        for(int i = head[u]; i != -1; i = edges[i].next)
        {
            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] = 1;
                    if(++cnt[e.to] > n + 1) return true;
                }
            }
        }
    }
    return false;
}

bool solve(int mid)
{
    for(int i = 0; i < ec; i++)
    {
        Edge& e = edges[i];
        if(e.from != 0) e.dist-=mid;
    }
    bool tmp = negative(mid);
    for(int i = 0; i < ec; i++)
    {
        Edge& e = edges[i];
        if(e.from !=0 ) e.dist+=mid;
    }
    return tmp;
}

int main()
{
    while(scanf("%d %d",&n,&m)!=EOF)
    {
        int low = 0;
        int high = -100010;
        init();
        for(int i = 1; i <= m; i++)
        {
            int from,to,dist;
            scanf("%d %d %d",&from,&to,&dist);
            add_edge(from,to,dist);
            if(dist > high) high = dist;
        }
        for(int i = 1; i <= n; i++)
        {
            add_edge(0,i,-100); //权值可以任意
        }
        if(!solve(high + 1)) printf("Infinite\n");
        else if(solve(1)) printf("No Solution\n");
        else
        {
            int ans = 1;
            while(low <= high)
            {
                int mid = low + (high - low)/2;
                if(!solve(mid))
                {
                    ans = mid;
                    low = mid + 1;
                }
                else high = 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、付费专栏及课程。

余额充值