HDU 2732 Leapin' Lizards

传送门

这道题应用了网络流,主要考的是怎么转化为网络流求解,重点是怎么建图。
题意是给你一个n*m网格,每个格子可能有一个柱子,也可能没柱子,每个柱子上有一个最大跳出次数,用完了这个柱子就废了,每个柱子上可能有一个蜥蜴(也可能没有,但只能最多存在一个(不仅初始是这样,过程中也要满足这一点)),蜥蜴的跳跃距离是d,蜥蜴只能从柱子跳到另一个柱子上,两个柱子的距离定义为abs(x1-x2)+abs(y1-y2),这个距离必须小于等于d才能跳。如果在当前柱子上跳跃一个合法距离可以跳出整个网格,那么这个蜥蜴就nb了,问你初始整个网格里面的蜥蜴有多少不nb。

柱子的这个次数暗示了点的限制条件,也暗示了这是个网络流问题(可以看出,在网络流中柱子只能是中间结点),运用拆点法思想,那么就把每个柱子拆成两个点,中间的连接边的权值就是这个次数。然后,我们要考虑柱子之间的跳跃关系,如果当前柱子的位置能跳出去,那么就把它和汇点连一条边(无容量限制),否则就和这个柱子能跳到的其他所有柱子都连一条边(根本不用考虑那个柱子还剩几次能跳,只要这两个柱子间距合法,就连边)(这个边无容量限制,但是要注意是当前柱子的后点连接其他柱子的前点(要不你那个柱子次数限制就不起作用了))。再然后,源点和初始有蜥蜴的柱子连一条权值为1的边(这个1表示这个柱子上有一只蜥蜴,就是网络中流来流去的东西)。

在这里插入图片描述
在这里插入图片描述
画个图吧,这个图对应样例中的第四个。

在实际代码中,我们没有判断当前柱子到达的那个位置点到底有没有柱子,不过这个无影响,因为如果那个位置点没柱子的话那条路肯定是死路、流不动。
还有一个问题,以上求解方法怎么保证每个柱子上最多只同时存在一只蜥蜴?
我的答案是,网络流的特性可以满足这一点,这些蜥蜴没必要同时去一个柱子(就算必须都去这个柱子),可以排队一个一个去。为什么呢?考虑最大流的EK算法,是不是每次找一条增广路然后循环多次?那我们就想象一个蜥蜴的行进路线是一条路,这个东西走完了下个东西再走也ok。

还有就是要细心看样例输出,有三种输出句子。

dinic

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <string>
#include <queue>
using namespace std;

const int MAX = 20 * 20 * 2 + 2 + 10;
const int INF = 1e9;
int T, n, m, d;
string str;

struct Edge
{
	int from, to, flow, cap;
};
vector<Edge> ve;
vector<int> v[MAX];
int level[MAX];
int cur[MAX];
int flow;
int lizards;

void init()
{
	ve.clear();
	for (int i = 0; i < MAX; i++)
		v[i].clear();
	flow = lizards = 0;
}

void addEdge(int from, int to, int weight)
{
	ve.push_back(Edge{ from,to,0,weight });
	ve.push_back(Edge{ to,from,0,0 });
	v[from].push_back(ve.size() - 2);
	v[to].push_back(ve.size() - 1);
}

bool bfs(int dst)
{
	queue<int> q;
	memset(level, -1, sizeof level);
	q.push(0);
	level[0] = 0;
	for (; !q.empty();)
	{
		int x = q.front();
		q.pop();
		for (int i = 0; i < v[x].size(); i++)
		{
			Edge& e = ve[v[x][i]];
			if (level[e.to] < 0 && e.flow < e.cap)
			{
				level[e.to] = level[x] + 1;
				q.push(e.to);
			}
		}
	}
	return level[dst] >= 0;
}

int dfs(int x, int dst, int f)
{
	if (x == dst || f == 0) return f;
	for (int& i = cur[x]; i < v[x].size(); i++)
	{
		Edge& e = ve[v[x][i]];
		int f0;
		if (level[e.to] == level[x] + 1 && (f0 = dfs(e.to, dst, min(f, e.cap - e.flow))) > 0)
		{
			e.flow += f0;
			ve[v[x][i] ^ 1].flow -= f0;
			return f0;
		}
	}
	return 0;
}

int main()
{
	scanf("%d", &T);
	for (int cnt = 1; cnt <= T; cnt++)
	{
		init();
		scanf("%d%d", &n, &d);
		for (int i = 0; i < n; i++)
		{
			cin >> str;
			m = str.size();
			for (int j = 0; j < m; j++)
			{
				if (str[j] != '0')
				{
					int id = i*m + j + 1, id0;
					addEdge(id, id + n*m, str[j] - '0');
					if (i - d < 0 || i + d > n - 1 || j - d < 0 || j + d > m - 1)
						addEdge(id + n*m, n*m * 2 + 1, INF);
					else
					{
						for (int ii = 0; ii < n; ii++)
						{
							for (int jj = 0; jj < m; jj++)
							{
								if (ii == i && jj == j) continue;
								if (abs(ii - i) + abs(jj - j) <= d)
								{
									id0 = ii*m + jj + 1;
									addEdge(id + n*m, id0, INF);
								}
							}
						}
					}
				}
			}
		}
		for (int i = 0; i < n; i++)
		{
			cin >> str;
			for (int j = 0; j < m; j++)
			{
				if (str[j] == 'L')
				{
					int id = i*m + j + 1;
					addEdge(0, id, 1);
					lizards++;
				}
			}
		}
		for (; bfs(n*m * 2 + 1);)
		{
			memset(cur, 0, sizeof cur);
			int temp;
			for (; temp = dfs(0, n*m * 2 + 1, INF);)
				flow += temp;
		}
		int ans = lizards - flow;
		if (ans == 0) printf("Case #%d: no lizard was left behind.\n", cnt);
		else if (ans == 1) printf("Case #%d: 1 lizard was left behind.\n", cnt);
		else printf("Case #%d: %d lizards were left behind.\n", cnt, ans);
	}

	return 0;
}

转载于:https://www.cnblogs.com/CrossingOver/p/10704875.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值