BZOJ 1066: [SCOI2007]蜥蜴 最大流

1066: [SCOI2007]蜥蜴


Time Limit: 1 Sec  Memory Limit: 162 MB
Submit: 4139  Solved: 2093
[Submit][Status][Discuss]
Description


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


Input


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


Output


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


Sample Input


5 8 2


00000000


02000000


00321100


02000000


00000000


........


........


..LLLL..


........


........
Sample Output


1


将每个高度大于0的点拆成两个点并连一条容量为h的边,对于距离d以内的点连一条容量为INF的边,源点向所有蜥蜴所在点 连一条容量为1的边,可以一步走出的点汇点连容量为INF的边,然后就是最大流了。           

#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<queue>
using namespace std;
const int maxm = 100005;
const int INF = 1e9 + 7;
struct node
{
	int data, flow, next;
}edge[maxm];
int map[30][30], head[maxm], dis[maxm], f[30][30], cur[maxm];
int n, m, s, t, cnt, d, id;
char str[30];
void init()
{
	cnt = 0, id = 0;
	memset(head, -1, sizeof(head));
}
void add(int u, int v, int w)
{
	edge[cnt].data = v, edge[cnt].flow = w, edge[cnt].next = head[u], head[u] = cnt++;
	edge[cnt].data = u, edge[cnt].flow = 0, edge[cnt].next = head[v], head[v] = cnt++;
}
void work(int x, int y)
{
	if (x - d<1 || y - d<1 || x + d>n || y + d>m) add(map[x][y] + id, t, INF);
	for (int i = max(1, x - d);i <= min(n, x + d);i++)
	{
		for (int j = max(1, y - d);j <= min(m, y + d);j++)
		{
			if (f[i][j] > 0 && abs(i - x) + abs(j - y) <= d)
				add(map[x][y] + id, map[i][j], INF);
		}
	}
}
int bfs()
{
	memset(dis, -1, sizeof(dis));
	queue<int>q;
	dis[s] = 0;
	q.push(s);
	while (!q.empty())
	{
		int u = q.front();q.pop();
		for (int i = head[u];i != -1;i = edge[i].next)
		{
			int v = edge[i].data;
			if (edge[i].flow > 0 && dis[v] == -1)
			{
				dis[v] = dis[u] + 1;
				q.push(v);
			}
		}
	}
	if (dis[t] == -1) return 0;
	return 1;
}
int dfs(int u, int flow)
{
	if (u == t) return flow;
	for (int i = cur[u];i != -1;i = edge[i].next)
	{
		int v = edge[i].data;
		if (edge[i].flow&&dis[v] == dis[u] + 1)
		{
			int d = dfs(v, min(flow, edge[i].flow));
			if (d > 0)
			{
				edge[i].flow -= d, edge[i ^ 1].flow += d;
				return d;
			}
		}
	}
	return 0;
}
int dinic()
{
	int ans = 0, d;
	while (bfs())
	{
		for (int i = s;i <= t;i++) cur[i] = head[i];
		while (d = dfs(s, INF) > 0)
			ans += d;
	}
	return ans;
}
int main()
{
	int i, j, k, sum = 0;
	scanf("%d%d%d", &n, &m, &d);
	init();
	for (i = 1;i <= n;i++)
	{
		scanf("%s", str + 1);
		for (j = 1;j <= m;j++)
		{
			f[i][j] = str[j] - '0';
			if (f[i][j] > 0) map[i][j] = ++id;
		}
	}
	s = 0, t = id * 2 + 1;
	for (i = 1;i <= n;i++)
	{
		scanf("%s", str + 1);
		for (j = 1;j <= m;j++)
			if (str[j] == 'L') { sum++, add(s, map[i][j], 1); }
	}
	for (i = 1;i <= n;i++)
	{
		for (j = 1;j <= m;j++)
			if (f[i][j] > 0)
			{
				add(map[i][j], map[i][j] + id, f[i][j]);
				work(i, j);
			}
	}
	printf("%d\n", sum - dinic());
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值