其他分享
首页 > 其他分享> > UVA12125 题解

UVA12125 题解

作者:互联网

UVA12125 题解

前言

一道经典的最大流问题,考察拆点和建图技巧

思路

对于每一个冰块上企鹅的数量,我们可以从源点 \(S\) 向每一个冰块连一条容量为该冰块上企鹅数量的边。

对于每一个冰块上所限制的起跳数量,直接建图不好建,考虑拆点,对于第 \(i\) 个冰块,我们将它拆成 \(i\) 和 \(n + i\) 两个冰块,此时从 \(i\) 节点向 \(i+1\) 节点连一条容量为对应限制的起跳数量的边,这样可以保证每一个冰块实际起跳次数总是不大于最大起跳次数。

对于每一对冰块,我们判断企鹅的最长跳跃距离和这一对冰块的距离,如果前者大于后者,则这对冰块应该连边,容量是几呢?因为我们已经保证每个冰块上所限制的起跳数量了,所以这里容量直接设成 \(INF\) 即可。

因为不知道最终企鹅会聚集在哪一个点 \(T\) ,那么我们枚举 \(T\) 的位置,对于每一个位置求一遍最大流,如果合法(标准见下一段),那么记录答案。

对于每个最大流,只需判断最大流和企鹅的数量,如果相等,那么说明所有企鹅都到了 \(T\) 点,合法;否则,有一些企鹅没有到达 \(T\) 点,不合法。

注意

Code

#include <iostream>
#include <cstring>

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

const int N = 210, M = 20410, INF = 1e8;
const double eps = 1e-8;

int n, S, T;
double D;
int h[N], e[M], f[M], ne[M], idx;
int q[N], depth[N], cur[N];
PII p[N];

bool check(PII a, PII b)
{
	double dx = a.x - b.x;
	double dy = a.y - b.y;
	return dx * dx + dy * dy < D * D + eps * eps; //精度问题
}

void add(int a, int b, int c)
{
	e[idx] = b;
	f[idx] = c;
	ne[idx] = h[a];
	h[a] = idx ++ ;
	
	e[idx] = a;
	f[idx] = 0;
	ne[idx] = h[b];
	h[b] = idx ++ ;
	
	return;
}

bool bfs()
{
	int hh = 0, tt = 0;
	memset(depth, -1, sizeof depth);
	q[0] = S;
	depth[S] = 0;
	cur[S] = h[S];
	
	while (hh <= tt)
	{
		int t = q[hh ++ ];
		for (int i = h[t]; ~i; i = ne[i])
		{
			int ver = e[i];
			if (depth[ver] == -1 && f[i] > 0)
			{
				depth[ver] = depth[t] + 1;
				cur[ver] = h[ver];
				q[ ++ tt] = ver;
				if (ver == T) return true;
			}
		}
	}
	
	return false;
}

int dfs(int u, int lmt)
{
	if (u == T) return lmt;
	int flow = 0;
	for (int i = cur[u]; ~i && flow < lmt; i = ne[i])
	{
		cur[u] = i;
		int ver = e[i];
		if (depth[ver] == depth[u] + 1 && f[i] > 0)
		{
			int t = dfs(ver, min(f[i], lmt - flow));
			if (!t) depth[ver] = -1;
			f[i] -= t;
			f[i ^ 1] += t;
			flow += t;
		}
	}
	
	return flow;
}

int dinic()
{
	int res = 0, flow = 0;
	while (bfs())
		while (flow = dfs(S, INF))
			res += flow;
	return res;
} //一直到这里都是板子

int main()
{
	int cases;
	cin >> cases;
	while (cases -- )
	{
		memset(h, -1, sizeof h); //记着初始化h
		idx = 0;
		cin >> n >> D;
		S = 0;
		
		int tot = 0;
		for (int i = 1; i <= n; i ++ )
		{
			int x, y, a, b;
			cin >> x >> y >> a >> b;
			p[i] = {x, y};
			add(S, i, a);
			add(i, n + i, b);
			tot += a;
		}
		
		for (int i = 1; i <= n; i ++ )
			for (int j = i + 1; j <= n; j ++ )
				if (check(p[i], p[j]))
				{
					add(n + i, j, INF);
					add(n + j, i, INF);
				}
        //对应思路,代码实现
		
        bool flag = 0;
		int cnt = 0;
		for (int i = 1; i <= n; i ++ )
		{
			T = i;
			for (int j = 0; j < idx; j += 2) //枚举正向边
			{
				f[j] += f[j ^ 1]; //将正向边弄成满流
				f[j ^ 1] = 0; //反向边弄成0,初始化
			}
			
			if (dinic() == tot)
			{
                if (cnt) cout << " ";
				cout << i - 1;
				cnt ++ ;
                flag = 1;
			}
		}
		
		if(!cnt) puts("-1");
		else puts("");
	}
	
	return 0;
}

标签:ver,冰块,idx,int,题解,UVA12125,depth,100
来源: https://www.cnblogs.com/LittleMoMol-kawayi/p/solution-LuoGu-UVA12125.html