uva 563 - Crimewave(网络流最大流)

题目链接:uva 563 - Crimewave


题目大意:在一个r*c的城市中,有n个银行,有一个抢匪想抢劫这n家银行,每次抢劫后需逃离城市(移动到坐标外),但是又不能走先前走过的位置,给出n个银行的坐标,问说抢匪能非成功抢劫银行并且逃脱。


解题思路:很经典的逃脱问题,要不是因为在刷网络流的专题根本想不到要用网络流去做。

首先先讲每个点与它周围的4个点建立一条边,表示可以移动。(注意要拆点,因为每个点都有容量限制,即只能走过一次,所以对于点c,要建立一个c‘,c’指向c有一条容量大小为1的边,所有与c点有关的点所建立的边统统指向c‘,而c点指向其他点的边任然保留在c上)

其次是建立汇点t,将所有的边界点指向汇点。

然后读入n个银行的坐标,对应的建立起点s与银行间的边。

最后是最大流问题,计算出从起点s到汇点t的最大流值ans,若ans等于n,说明成功,否则失败。


昨天晚上做一一晚上这道题目,一直RE,最后到会宿舍才发现说在拆点的时候,c ->c’ = c + T; T = R * C才对,应该是2500,但我一直以为是250,郁闷啊~


还有这道题应该用邻接表的来储存关系,不过因为做得太纠结,就懒得改了,数组开太大,时间有点久。


#include <stdio.h>
#include <string.h>
#include <queue>

#define min(a,b) (a)<(b)?(a):(b)
#define max(a,b) (a)>(b)?(a):(b)

using namespace std;

const int N = 5010;
const int T = 2500;
const int INF = 1 << 20;
const int dir[10][2] = { {0, 1}, {1, 0}, {0, -1}, {-1, 0} };

int r, c, n, tmp;
int p[N][N], f[N][N];
int g[N][T + 10], son[N];

void add(int x, int y) {
	p[x][y] = 1;
	g[x][son[x]++] = y;
}

void init() {

	memset(g, 0, sizeof(g));
	memset(son, 0, sizeof(son));

	memset(p, 0, sizeof(p));
	memset(f, 0, sizeof(f));

	scanf("%d%d%d", &r, &c, &n);
	tmp = 5001;

	for (int i = 1; i <= r; i++) {

		for (int j = 1; j <= c; j++) {

			if (i == 1 || j == 1 || i == r || j == c)
				add( (i-1) * c + j, 0);

			add( (i-1) * c + j + T, (i-1) * c + j);

			for (int k = 0; k < 4; k++) {

				int p = i + dir[k][0], q = j + dir[k][1];

				if (p <= 0 || p > r) continue;
				if (q <= 0 || q > c) continue;

				add( (i-1) * c + j, (p-1) * c + q + T);
			}
		}
	}

	int a, b;
	for (int i = 0; i < n; i++) {

		scanf("%d%d\n", &a, &b);
		add(tmp, (a-1) * c + b + T);	
	}
}

int solve() {
	int ans = 0;
	int k, s[N], path[N];
	queue<int> que;

	while (1) {

		memset(path, 0, sizeof(path));
		memset(s, 0, sizeof(s));

		path[tmp] = tmp;
		s[tmp] = INF;
		que.push(tmp);

		while (!que.empty() ) {
			k = que.front(), que.pop();

			for (int i = 0; i < son[k]; i++) {


				if ( !s[g[k][i]] && p[k][g[k][i]] > f[k][g[k][i]]) {

					s[g[k][i]] = min(s[k], p[k][g[k][i]] - f[k][g[k][i]]);
					path[g[k][i]] = k;
					que.push(g[k][i]);
				}
			}
		}

		if (s[0] == 0) break;

		ans += s[0];
		for (int i = 0; i != tmp; i = path[i]) {
			f[path[i]][i] += s[0];
			f[i][path[i]] -= s[0];
		}
	}
	return ans;
}

int main () {
	int cas;
	scanf("%d", &cas);
	while (cas--) {
		init();

		int ans = solve();
		printf("%s\n", ans == n ? "possible" : "not possible");
	}
	return 0;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值