UVA - 11478 Halum 【二分答案+spfa】

版权声明:如需转载,记得标识出处 https://blog.csdn.net/godleaf/article/details/88076818

题目链接:https://cn.vjudge.net/problem/UVA-11478

这道题有两点收获。

1,stack的spfa的确要比queue的spfa快很多,如果发现queue的spfa超时的话,可以试试用stack。

2,之前的spfa模板给记错了,我以为松弛的次数是边的数目,今天才发现松弛的次数应该是点的数目,在搞错这个的情况下,我用stack就AC了,之前一直T,我是和别人的代码一行行对着看才发现的,哭笑不得。。

这道题在题意上估计很多人都很懵,至少我在看这题的时候连样例的答案都不清楚是怎么来的。

首先有两点信息是很明确的,第一,Halum(v, d) 这个函数的实现过程就是所有指向v的边都减d,而所有从v指出去的边都加d。第二点是题目给出每条边的信息。

题目最后一段话的意思,我一开始理解成至少存在一个大于0的边就行,但真正的意思是,所有的边都要大于0才行,如果没有at least我可能还能读明白。我们要求的cost就是在Halum的影响下,所有边中最小且能满足条件的边权是多少,这个边权要尽可能的大。

如果整体去考虑很困难的话,可以试试把问题的范围缩小一点考虑,假设我就只有3个点,1,2,3,有两条边(1, 2), (2, 3)

每一条有向边都有可能存在加某个值或者减某个值的情况,比如说对点1做Halum操作,那么(1, 2) 这条边就会加上某个值y,即+y,如果我对点2做Halum操作,那么 (1, 2) 这条边就会减去某个值x,即-x,那么(1, 2)这条边最终需要减或者加多少?

(1, 2) + (-x + y)。根据这个规律,我们可以得出一个结论,我不管过程怎么样,不管你哪个点做几次Halum的操作,哪个边加了几次加了多少减了几次减了多少,我如果想要满足题目的要求,对与任何一条边 (u, v),我都应该满足 (u, v) + d > 0,当然这个d可能是正的,也可能是负的,甚至可能是0,我不管d是由哪几个值合并得到的,我只要知道合并的值d满足上面的公式就行。因为题目要求的cost要尽可能的大,而0是题目给出的下限,至于上限,即使题目没有明说,我们也能够知道上限是所有边权最大的那个。

因为答案很明显存在单调性,也就是说如果x满足条件,那么x-1,x-2也一定满足,那么就能通过二分答案的方法去枚举答案。

假设我枚举满足条件的最小边权是 x,那么对于边 (u, v),我必须满足 w(u, v)+d >= x,即边权w在经过未知次数的Halum后得到的值大于或者等于x,那么得到的不等式就是 d >= x-w(u, v),那么给点u和v构图的权值就应该是 x-w(u, v)。

因为之前的博客已经解释过这类差分约束问题的原理了,这里就不重复说了,不知道差分约束的朋友可以先百度了解下。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <queue>
#include <string>

using namespace std;

const int Maxn = 3e3+10;
const int INF = 0x3f3f3f3f;

struct Edge {
	int v, w, next;
	Edge(int x = 0, int y = 0): v(x), w(y){
	}
} edge[Maxn];

int d[505], edge_cnt, head[505], cnt[505];
bool vis[505];

void add(int from, int to, int w) {
    edge[edge_cnt].v = to;
    edge[edge_cnt].w = w;
    edge[edge_cnt].next = head[from];
    head[from] = edge_cnt++;
}

bool spfa(int x, int n) {
	memset(vis, true, sizeof(vis));
	memset(cnt, 0, sizeof(cnt));
	memset(d, 0, sizeof(d));
	queue<int> qu;
	for(int i = 1; i <= n; ++i)
        qu.push(i);
	while (!qu.empty()) {
		int v = qu.front(); qu.pop();
		vis[v] = false;

		for(int i = head[v]; i != -1; i = edge[i].next) {
			Edge &e = edge[i];
			if(d[e.v] < d[v]+(x-e.w)) {
				d[e.v] = d[v]+(x-e.w);
                if(!vis[e.v]) {
                    vis[e.v] = true;
                    qu.push(e.v);
                    if(++cnt[e.v] > n) return false;
                }
			}
		}
	}
    return true;
}

int main (void)
{
	int N, E;
	while(scanf("%d%d", &N, &E) != EOF) {
        memset(head, -1, sizeof(head));
        edge_cnt = 0;
        int u, v, w, maxn = 0;
        for(int i = 0; i < E; ++i) {
            scanf("%d%d%d", &u, &v, &w);
            maxn = max(maxn, w);
            add(u, v, w);
        }
        if(spfa(maxn+1, N)) printf("Infinite\n");
        else if(!spfa(1, N)) printf("No Solution\n");
        else {
            int L = 0, R = maxn, mid;
            while(L < R) {
                mid = (L+R+1)/2;
                if(spfa(mid, N)) L = mid;
                else R = mid-1;
            }
            printf("%d\n", L);
        }
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/godleaf/article/details/88076818
今日推荐