UVA - 11478 Halum 二分+差分约束

题目链接:

http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=34651

题意:

给定一个有向图,每一条边都有一个权值,每次你可以选择一个节点v和一个整数d,把所有以v结尾的边权值减小d,把所有以v为起点的边的权值增加d,最后要让所有边权的最小值大于0且尽量大。

题解:

最小值最大,可以用二分,这样可以得到一个差分约束系统,然后每次都用最短路跑。

代码:

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

const int maxn = 555;
const int maxm = 2777;

struct Edge {
    int v, w;
    Edge(int v, int w) :v(v), w(w) {}
    Edge() {}
};

vector<Edge> egs;
vector<int> G[maxn];
int tot = 0;
int n, m;

void addEdge(int u, int v, int w) {
    egs.push_back(Edge(v, w));
    tot = egs.size();
    G[u].push_back(tot - 1);
}

bool inq[maxn];
int d[maxn];
int cnt[maxn];
bool spfa(int x) {
    bool ret = true;

    for (int i = 1; i < n; i++) {
        for (int j = 0; j < G[i].size(); j++) {
            Edge& e = egs[G[i][j]];
            e.w -= x;
        }
    }
    memset(inq, 0, sizeof(inq));
    memset(cnt, 0, sizeof(cnt));
    memset(d, 0x3f, sizeof(d));

    queue<int> Q;
    d[0] = 0; inq[0] = true; Q.push(0);
    while (!Q.empty()) {
        int u = Q.front(); Q.pop();
        inq[u] = false;
        for (int i = 0; i < G[u].size(); i++) {
            Edge& e = egs[G[u][i]];
            if (d[e.v] > d[u] + e.w) {
                d[e.v] = d[u] + e.w;
                if (!inq[e.v]) {
                    Q.push(e.v); inq[e.v] = true;
                    if (++cnt[e.v] > n) {
                        ret = false; break;
                    }
                }
            }
        }
        if (ret == false) break;
        //printf("u:%d\nx:%d\n", u,x);
        //printf("in circle\n");
    }

    for (int i = 1; i < n; i++) {
        for (int j = 0; j < G[i].size(); j++) {
            Edge& e = egs[G[i][j]];
            e.w += x;
        }
    }

    return ret;
}

void init() {
    for (int i = 0; i < n; i++) G[i].clear();
    egs.clear();
}

int main() {
    while (scanf("%d%d", &n, &m) == 2 && n) {
        n++;
        init();
        int l = 1, r = -1;
        for (int i = 0; i < m; i++) {
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            r = max(r, w);
            addEdge(u, v, w);
        }
        for (int i = 1; i < n; i++) {
            addEdge(0, i, 0);
        }
        r++;
        if (!spfa(1)) {
            printf("No Solution\n");
        }
        else if (spfa(r)) {
            printf("Infinite\n");
        }
        else {
            while (l + 1 < r) {
                int mid = l + (r - l) / 2;
                if (spfa(mid)) l = mid;
                else r = mid;
                //printf("here!\n");
            }
            printf("%d\n", l);
        }

    }
    return 0;
}

/*
1
2 10
1
2 -10
3
2 4
3 2
1 5
5
3 4
2 5
4 2
1 0
2 -1
*/

 

转载于:https://www.cnblogs.com/fenice/p/5532549.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值