2020 年百度之星·程序设计大赛 - 初赛一hdu-6749 Mosquito (二分,最大流,优化建图)

hdu-6749 Mosquito (二分,最大流,优化建图)

2020 年百度之星·程序设计大赛 - 初赛一0 Mosquito

Problem Description

房间是个 n∗m 的网格,一共有 k 个窗户,都在上下左右四条边上。在第 0 时刻,每个窗户对应的格子上都会出现若干只蚊子。

蚊子每个时刻可以往上下左右移动一格或者呆在原地不动。

假设这些蚊子都足够聪明,请问最少花费多少时刻,使得所有格子上都有至少一只蚊子?

蚊子在第 0 时刻不能动。

思路:

\(\sum_{i=1}^k z_i < n*m\)答案为-1,其他均有答案。

设花费\(t_0\)时刻可以满足条件,那么\(t_0+1\)一定也满足,则具有单调性,所以答案的时间\(\mathit t\) 可以二分求出。

问题转化为检验型问题,即给定一个时间\(\mathit t\),问能否满足所有格子都有蚊子。

该问题可以通过建流量网络求最大流来解决。

建图方法:

设源点为\(\mathit S\),汇点\(\mathit T\)

1、源点\(\mathit S\)对每一个窗户\(W_i\),建立流量为\(z_i\)的有向边。

2、每一个窗口\(W_i\)对距离其位置曼哈顿距离小于\(\mathit t\) 的格点建立流量为\(\text 1\)的有向边。

3、每一个格点对汇点\(\mathit T\)建立流量为\(\text 1\)的边。

\(Flow\)为从源点到汇点的最大流,那么如果\(n*m\le Flow\)则满足条件。

我们知道这样建立的网络流的节点个数是\(n*m+k+2\),用任何高效的最大流算法都是无法在时限内求出的。

那么我们来思考如何优化建图,

我们仔细思考(画图更容易理解),这\(n*m\)个格点,有很多节点的入边,出边,以及它们的流量都是完全相等的。

我们知道,在流量网络中,把这种非汇源点且入边和出边完全相等的节点们(假设\(num\)个)浓缩成一个节点,节点的每一个边的流量值扩大\(num\)倍,对源点到汇点的最大流是没有影响的。

那么我们的网络中,每一个格子节点的出边都是一样的,那么的区别只在于入边,

\(k\leq 6\),我们可以用二进制状态压缩代表一个节点的入边有哪些窗户,然后状态相等的节点就可以缩成一个节点。

细节见代码:

code:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <bits/stdc++.h>
#define ALL(x) (x).begin(), (x).end()
#define sz(a) int(a.size())
#define rep(i,x,n) for(int i=x;i<n;i++)
#define repd(i,x,n) for(int i=x;i<=n;i++)
#define pii pair<int,int>
#define pll pair<long long ,long long>
#define gbtb ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define MS0(X) memset((X), 0, sizeof((X)))
#define MSC0(X) memset((X), '\0', sizeof((X)))
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define eps 1e-6
#define chu(x)  if(DEBUG_Switch) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
#define du3(a,b,c) scanf("%d %d %d",&(a),&(b),&(c))
#define du2(a,b) scanf("%d %d",&(a),&(b))
#define du1(a) scanf("%d",&(a));
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
ll lcm(ll a, ll b) {return a / gcd(a, b) * b;}
ll powmod(ll a, ll b, ll MOD) { if (a == 0ll) {return 0ll;} a %= MOD; ll ans = 1; while (b) {if (b & 1) {ans = ans * a % MOD;} a = a * a % MOD; b >>= 1;} return ans;}
ll poww(ll a, ll b) { if (a == 0ll) {return 0ll;} ll ans = 1; while (b) {if (b & 1) {ans = ans * a ;} a = a * a ; b >>= 1;} return ans;}
void Pv(const vector<int> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%d", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("\n");}}}
void Pvl(const vector<ll> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%lld", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("\n");}}}
inline long long readll() {long long tmp = 0, fh = 1; char c = getchar(); while (c < '0' || c > '9') {if (c == '-') fh = -1; c = getchar();} while (c >= '0' && c <= '9') tmp = tmp * 10 + c - 48, c = getchar(); return tmp * fh;}
inline int readint() {int tmp = 0, fh = 1; char c = getchar(); while (c < '0' || c > '9') {if (c == '-') fh = -1; c = getchar();} while (c >= '0' && c <= '9') tmp = tmp * 10 + c - 48, c = getchar(); return tmp * fh;}
void pvarr_int(int *arr, int n, int strat = 1) {if (strat == 0) {n--;} repd(i, strat, n) {printf("%d%c", arr[i], i == n ? '\n' : ' ');}}
void pvarr_LL(ll *arr, int n, int strat = 1) {if (strat == 0) {n--;} repd(i, strat, n) {printf("%lld%c", arr[i], i == n ? '\n' : ' ');}}
const int maxn = 1010;
const int inf = 0x3f3f3f3f;
/*** TEMPLATE CODE * * STARTS HERE ***/
#define DEBUG_Switch 0
#define N 100
#define INF 1e13
struct Edge
{
	int from, to;
	ll  cap, flow; // cap 并不减少,只是flow在增,有残余就是cap>flow
};
int si = 0;
struct ISAP
{
	int n, m, s, t;
	vector<Edge>edges;
	vector<int>G[N];
	bool vis[N];
	int d[N], cur[N];
	int p[N], num[N]; //比Dinic算法多了这两个数组,p数组标记父亲结点,num数组标记距离d[i]存在几个
	void addedge(int from, int to, ll cap)
	{
		edges.push_back((Edge) {from, to, cap, 0});
		edges.push_back((Edge) {to, from, 0, 0});
		int m = edges.size();
		G[from].push_back(m - 2);
		G[to].push_back(m - 1);
	}
	void init()
	{
		MS0(d);
		edges.clear();
		for (int i = 0; i <= n; ++i) {
			G[i].clear();
		}
	}
	ll Augumemt()
	{
		ll x = t, a = INF;
		while (x != s) //找最小的残量值
		{
			Edge&e = edges[p[x]];
			a = min(a, e.cap - e.flow);
			x = edges[p[x]].from;
		}
		x = t;
		while (x != s) //增广
		{
			edges[p[x]].flow += a;
			edges[p[x] ^ 1].flow -= a;
			x = edges[p[x]].from;
		}
		return a;
	}
	void bfs()//逆向进行bfs
	{
		memset(vis, 0, sizeof(vis));
		queue<int>q;
		q.push(t);
		d[t] = 0;
		vis[t] = 1;
		while (!q.empty())
		{
			int x = q.front(); q.pop();
			int len = G[x].size();
			for (int i = 0; i < len; i++)
			{
				Edge&e = edges[G[x][i]];
				if (!vis[e.from] && e.cap > e.flow)
				{
					vis[e.from] = 1;
					d[e.from] = d[x] + 1;
					q.push(e.from);
				}
			}
		}
	}

	ll Maxflow(int s, int t) //根据情况前进或者后退,走到汇点时增广
	{
		this->s = s;
		this->t = t;
		ll flow = 0;
		bfs();
		memset(num, 0, sizeof(num));
		for (int i = 0; i < n; i++)
			num[d[i]]++;
		int x = s;
		memset(cur, 0, sizeof(cur));
		while (d[s] < n)
		{
			if (x == t) //走到了汇点,进行增广
			{
				flow += Augumemt();
				x = s; //增广后回到源点
			}
			int ok = 0;
			for (int i = cur[x]; i < G[x].size(); i++)
			{
				Edge&e = edges[G[x][i]];
				if (e.cap > e.flow && d[x] == d[e.to] + 1)
				{
					ok = 1;
					p[e.to] = G[x][i]; //记录来的时候走的边,即父边
					cur[x] = i;
					x = e.to; //前进
					break;
				}
			}
			if (!ok) //走不动了,撤退
			{
				int m = n - 1; //如果没有弧,那么m+1就是n,即d[i]=n
				for (int i = 0; i < G[x].size(); i++)
				{
					Edge&e = edges[G[x][i]];
					if (e.cap > e.flow)
						m = min(m, d[e.to]);
				}
				if (--num[d[x]] == 0)break; //如果走不动了,且这个距离值原来只有一个,那么s-t不连通,这就是所谓的“gap优化”
				num[d[x] = m + 1]++;
				cur[x] = 0;
				if (x != s)
					x = edges[p[x]].from; //退一步,沿着父边返回
			}
		}
		return flow;
	}
	//  调用前给这里的n赋值,一般为n=T+1;
} gao;
/*
    int S = 0;
    int T = n + 10;
    gao.n = T + 1;
    gao.addedge(S, i, c[i]);
    gao.Maxflow(S, T);
    gao.init();
*/
int n, m;
int k;
int info[maxn][maxn];
int x[maxn];
int y[maxn];
int w[maxn];
int cnt[maxn];
bool check(int t)
{
	repd(i, 1, n)
	{
		repd(j, 1, m)
		{
			info[i][j] = 0;
		}
	}
	repd(q, 0, k - 1)
	{
		repd(i, max(1, x[q] - t), min(n, x[q] + t))
		{
			repd(j, max(1, y[q] - t), min(m, y[q] + t))
			{
				if (abs(i - x[q]) + abs(j - y[q]) <= t)
				{
					info[i][j] |= (1 << q);
				}
			}
		}
	}
	
	int maxstate = (1 << k) - 1;
	repd(i, 0, maxstate)
	{
		cnt[i] = 0;
	}
	repd(i, 1, n)
	{
		repd(j, 1, m)
		{
			cnt[info[i][j]]++;
		}
	}

	if (cnt[0] > 0)
		return 0; // 小小的优化
	int T = maxstate + 10;
	int S = 0;
	gao.n = T + 1;
	/*
	gao.addedge(S, i, c[i]);
	gao.Maxflow(S, T);
	 */
	repd(j, 0, k - 1)
	{
		gao.addedge(S, maxstate + j + 1, w[j]);
	}
	repd(i, 1, maxstate)
	{
		for (int j = 0; j < k; ++j)
		{
			if (i & (1 << j))
			{
				gao.addedge(maxstate + j + 1, i, w[j]);
			}
		}
		gao.addedge(i, T, cnt[i]);
	}
	int res = gao.Maxflow(S, T);
	gao.init();
	return res >= n * m;
}
int main()
{
#if DEBUG_Switch
	freopen("C:\\code\\input.txt", "r", stdin);
#endif
	//freopen("C:\\code\\output.txt","w",stdout);
	int t;
	t = readint();
	while (t--)
	{
		n = readint(); m = readint();
		k = readint();
		repd(i, 1, n)
		{
			repd(j, 1, m)
			{
				info[i][j] = 0;
			}
		}
		int sum = 0;
		repd(i, 0, k - 1)
		{
			x[i] = readint();
			y[i] = readint();
			w[i] = readint();
			sum += w[i];
		}
		if (n * m > sum)
		{
			printf("-1\n");
			continue;
		}
		int l = 0;
		int r = m + n - 2;
		int mid;
		int ans;
		while (l <= r)
		{
			mid = (l + r) >> 1;
			if (check(mid))
			{
				ans = mid;
				r = mid - 1;
			} else
			{
				l = mid + 1;
			}
		}
		printf("%d\n", ans );
	}

	return 0;
}
posted @ 2020-07-22 01:28  茄子Min  阅读(202)  评论(0编辑  收藏  举报