Loading

[SCOI2007]蜥蜴 (网络最大流+拆点)

刚学习网络流
一上午熟悉\(Dinic\)板子+就调过了这一个题

题目描述

在一个r行c列的网格地图中有一些高度不同的石柱,一些石柱上站着一些蜥蜴,你的任务是让尽量多的蜥蜴逃到边界外。 每行每列中相邻石柱的距离为1,蜥蜴的跳跃距离是d,即蜥蜴可以跳到平面距离不超过d的任何一个石柱上。石柱都不稳定,每次当蜥蜴跳跃时,所离开的石柱高度减1(如果仍然落在地图内部,则到达的石柱高度不变),如果该石柱原来高度为1,则蜥蜴离开后消失。以后其他蜥蜴不能落脚。任何时刻不能有两只蜥蜴在同一个石柱上。

输入格式

输入第一行为三个整数r,c,d,即地图的规模与最大跳跃距离。以下r行为石竹的初始状态,0表示没有石柱,1~3表示石柱的初始高度。以下r行为蜥蜴位置,“L”表示蜥蜴,“.”表示没有蜥蜴。

输出格式

输出仅一行,包含一个整数,即无法逃离的蜥蜴总数的最小值。

样例

样例输入

5 8 2
00000000
02000000
00321100
02000000
00000000
........
........
..LLLL..
........
........

样例输出

1

数据范围与提示

100%的数据满足:1<=r, c<=20, 1<=d<=4

Solution

看上去就是每个点有限制能跳过几只蜥蜴
就相当于边权中的流量上限
考虑如何建图
由于对于单个点无法增加边权
选择拆点成边
拆出的点之间的边权就是流量的限制
首先建立超级源点\(s=0\)
因为图中有\(n*m\)个点
拆完之后\(2*n*m\)个点
所以超级汇点是\(t=2*n*m+1\)
图中建边有四种

  • 由源点\(s\)出发,向所有蜥蜴所在的点建立边权为\(1\)的边
  • 所有有高度(就是蜥蜴可以跳跃的点)拆点,向\((i,j)\)对应的点\((i,j) + n*m\)建边,边权为当前点的高度,即流量限制
  • 由所有一次能够跳出矩阵的点向超级汇点\(t\)建边,边权无限制,为\(inf\)
  • 矩阵内部互相能够到达的点,边权无限制,为\(inf\)

最后直接跑\(Dinic\)就行了
注意\(Dinic\)跑出来的是能够逃离的蜥蜴的数量
要用总数量减一下
拆出来的点,一个是原点,一个是新点
原点负责所有向外建边
新点负责其他点向自己建边
这个建图的时候要注意一下
上午调了好久~

Code

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#define min(a, b) ({register int AA = a, BB = b; AA < BB ? AA : BB;})
#define inf 10000009
using namespace std;

inline int read(){
	int x = 0, w = 1;
	char ch = getchar();
	for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') w = -1;
	for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
	return x * w;
}

const int ss = 10010;

struct node{
	int to, nxt, w;
}edge[ss * 20];

int head[ss], tot = 1;
inline void add(register int u, register int v, register int w){
	edge[++tot].to = v;
	edge[tot].nxt = head[u];
	edge[tot].w = w;
	head[u] = tot;
}

int dis[ss], cur[ss];
int r, c, d, s, t;
bool vis[ss];
queue<int> q;
inline bool spfa(register int s){
	for(register int i = 0; i <= t; i++)
		vis[i] = 0, dis[i] = 0x3f3f3f3f, cur[i] = head[i];
	dis[s] = 0;
	q.push(s);
	while(!q.empty()){
		register int u = q.front();
		q.pop();
		vis[u] = 0;
		for(register int i = head[u]; i; i = edge[i].nxt){
			register int v = edge[i].to;
			if(dis[v] > dis[u] + 1 && edge[i].w){
				dis[v] = dis[u] + 1;
				if(!vis[v]) q.push(v), vis[v] = 1;
			}
		}
	}
	return dis[t] != 0x3f3f3f3f;
}

inline int dfs(register int u, register int flow){
	register int res = 0;
	if(u == t) return flow;
	for(register int i = cur[u]; i; i = edge[i].nxt){
		cur[u] = i;
		register int v = edge[i].to;
		if(dis[v] == dis[u] + 1 && edge[i].w){
			if(res = dfs(v, min(flow, edge[i].w))){
				edge[i].w -= res;
				edge[i ^ 1].w += res;
				return res;
			}
		}
	}
	return 0;
}

long long ans, cnt;
inline long long dinic(){
	register long long minflow = 0;
	while(spfa(s)){
		while(minflow = dfs(s, 0x7fffffff))
			ans += minflow;
	}
	return ans;
}

inline double getdis(register int a, register int b, register int c, register int d){
	return sqrt((a - c) * (a - c) + (b - d) * (b - d));
}

inline int change(register int i, register int j){
	return (i - 1) * c + j;
}

int a[25][25], flag[25][25];
char ch[25];
signed main(){
	r = read(), c = read(), d = read();
	for(register int i = 1; i <= r; i++){
		scanf("%s", ch + 1);
		for(register int j = 1; j <= c; j++)
			a[i][j] = ch[j] - '0';
	}
	for(register int i = 1; i <= r; i++){
		scanf("%s", ch + 1);
		for(register int j = 1; j <= c; j++)
			if(ch[j] == 'L') flag[i][j] = 1;
	}
	
	s = 0, t = 2 * c * r + 1;
	for(register int i = 1; i <= r; i++)
		for(register int j = 1; j <= c; j++)
			if(flag[i][j] == 1){
				cnt++;
				add(s, change(i, j), 1);
				add(change(i, j), s, 0);
			}
	for(register int i = 1; i <= r; i++)
		for(register int j = 1; j <= c; j++)
			if(a[i][j]){
				add(change(i, j), change(i, j) + r * c, a[i][j]);
				add(change(i, j) + r * c, change(i, j), 0);
			}
	for(register int i = 1; i <= r; i++)
		for(register int j = 1; j <= c; j++){
			if(i > d && i <= r - d && j > d && j <= c - d) continue;
			if(a[i][j]){
				add(change(i, j) + r * c, t, inf);
				add(t, change(i, j) + r * c, 0);
			}
		}
	for(register int i = 1; i <= r; i++){
		for(register int j = 1; j <= c; j++){
			for(register int p = 1; p <= r; p++){
				for(register int q = 1; q <= c; q++){
					if(i == p && j == q) continue;
					if(getdis(i, j, p, q) <= (double)d && a[i][j] && a[p][q]){
						add(change(i, j) + r * c, change(p, q), inf);
						add(change(p, q), change(i, j) + r * c, 0);
					}
				}
			}
		}
	}
	printf("%lld\n", cnt - dinic());
	return 0;
}
posted @ 2020-11-12 11:59  Gary_818  阅读(153)  评论(2编辑  收藏  举报