CSU 1355 地雷清除计划 网格图清除最少的地雷使得对角联通 最小割

题目链接:点击打开链接

Description

现在有一个由NM列个单元格组成的游戏地图,从上至下依次记为第1行,第2行,……,第N行,从左至右依次记为第1列,第2列,……,第M列。不妨用(xy)来表示第x行第y列的那个单元格。

目前你位于(1, 1),而你想前往(NM)并跳转到下一个地图中,但是这幅地图上却遍布着一些相同地雷,地雷的感知半径为K,如果你踏入的单元格距离某颗地雷的曼哈顿距离不超过K,那么这颗地雷就会爆炸并炸伤你,游戏也就结束了。不过幸运的是你可以用一枚金币的代价清除这个地图上任意一颗地雷,那么你最少需要使用多少枚金币来清除地雷才能保证顺利从(1, 1)走到(NM)呢?你在任何一个单元格时,都可以向上、向下、向左或者向右走到相邻的单元格,但是不能走到地图之外。

Input

输入的第一行包含一个整数T (1 <= T <= 100),表示一共有T组测试数据。

对于每组测试数据,第一行包含三个整数NMK ( 1 <= NM<= 50, 0<= K < 30),含义同上。接下来一共有N行,每行均有M个字符,描述了这个游戏地图。其中’.’表示没有地雷的单元格,’*’表示有地雷的单元格(如果该地雷被清除了,那么这个格子就会变成没有地雷的单元格)。数据保证地雷的总数量不超过200,(1, 1)和(NM)距离任何一颗地雷的曼哈顿距离都大于K

Output

对于每组测试数据,用一行输出一个整数,表示至少需要使用多少枚金币来清除地雷。

Sample Input

3
5 5 1
..*..
....*
.....
*....
..*..
4 6 1
..*...
......
......
...*..
3 3 0
.**
***
**.

Sample Output

0
1
3

思路:

我们把所有能互相影响的地雷连线(所谓互相影响就是两个地雷的爆炸范围会覆盖或者把路堵死)

那么连线以后只有以下4种线是会拦路的:

1、左边界和上边界相连的线

2、左边界和有边界相连的线

3、下边界和上边界相连的线

4、下边界和有边界相连的线

其实我们要做的事情就是尽可能少地剪断这样线,使得剪断后不存在这样的线能使左下边界和右上边界相连

把左下边界和右上边界简化成一个点,就是求个最小割。

类似于这题点击打开链接

#include <cstdio>
#include <algorithm>
#include <string.h>
#include <queue>
#include <cstring>
#include <cmath>
#include <iostream>
#include <vector>
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
//点标 [0,n]
const int N = 1005;
const int M = 500010;
const int INF = ~0u >> 2;
template<class T>
struct Max_Flow {
	int n;
	int Q[N], sign;
	int head[N], level[N], cur[N], pre[N];
	int nxt[M], pnt[M], E;
	T cap[M];
	void Init(int n) {
		this->n = n + 1;
		E = 0;
		std::fill(head, head + this->n, -1);
	}
	//有向rw 就= 0
	void add(int from, int to, T c, T rw = 0) {
		pnt[E] = to;
		cap[E] = c;
		nxt[E] = head[from];
		head[from] = E++;

		pnt[E] = from;
		cap[E] = rw;
		nxt[E] = head[to];
		head[to] = E++;
	}
	bool Bfs(int s, int t) {
		sign = t;
		std::fill(level, level + n, -1);
		int *front = Q, *tail = Q;
		*tail++ = t; level[t] = 0;
		while (front < tail && level[s] == -1) {
			int u = *front++;
			for (int e = head[u]; e != -1; e = nxt[e]) {
				if (cap[e ^ 1] > 0 && level[pnt[e]] < 0) {
					level[pnt[e]] = level[u] + 1;
					*tail++ = pnt[e];
				}
			}
		}
		return level[s] != -1;
	}
	void Push(int t, T &flow) {
		T mi = INF;
		int p = pre[t];
		for (int p = pre[t]; p != -1; p = pre[pnt[p ^ 1]]) {
			mi = std::min(mi, cap[p]);
		}
		for (int p = pre[t]; p != -1; p = pre[pnt[p ^ 1]]) {
			cap[p] -= mi;
			if (!cap[p]) {
				sign = pnt[p ^ 1];
			}
			cap[p ^ 1] += mi;
		}
		flow += mi;
	}
	void Dfs(int u, int t, T &flow) {
		if (u == t) {
			Push(t, flow);
			return;
		}
		for (int &e = cur[u]; e != -1; e = nxt[e]) {
			if (cap[e] > 0 && level[u] - 1 == level[pnt[e]]) {
				pre[pnt[e]] = e;
				Dfs(pnt[e], t, flow);
				if (level[sign] > level[u]) {
					return;
				}
				sign = t;
			}
		}
	}
	T Dinic(int s, int t) {
		pre[s] = -1;
		T flow = 0;
		while (Bfs(s, t)) {
			std::copy(head, head + n, cur);
			Dfs(s, t, flow);
		}
		return flow;
	}
};
Max_Flow <int>F;
typedef pair<int, int> pii;
#define pb push_back
const int MAXN = 55;
int n, m, r, from, to;
char mp[MAXN][MAXN];
vector<pii>G;
int dis(pii x, pii y){
	return abs(x.first - y.first) + abs(x.second - y.second);
}
void build(){
	from = G.size() * 2 + 1, to = from + 1;
	F.Init(to);
	for (int i = 0; i < G.size(); i++){
		int x = G[i].first, y = G[i].second;
		F.add(i * 2, i * 2 + 1, 1);

		if (y - r <= 1 || x + r >= n)//碰到左边界或下边界
			F.add(from, i * 2, INF);
		if (y + r >= m || x - r <= 1)//碰到右边界或上边界
			F.add(i * 2 + 1, to, INF);
		for (int j = i+1; j < G.size(); j++)
		{
			bool touch = false;
			if ((x == G[j].first || y == G[j].second) && dis(G[i], G[j]) <= 2 * r + 1)touch = true;
			if (x != G[j].first && y != G[j].second && dis(G[i], G[j]) <= 2 * r + 2)touch = true;
			if (touch){
				F.add(i * 2 + 1, j * 2, INF);
				F.add(j * 2 + 1, i * 2, INF);
			}
		}
	}
}
void input(){
	G.clear();
	scanf("%d %d %d", &n, &m, &r);
	for (int i = 1; i <= n; i++)
	{
		scanf("%s", mp[i] + 1);
		for (int j = 1; j <= m; j++)
		if (mp[i][j] == '*')
			G.pb(pii(i, j));
	}
}
int main(){
	int T; scanf("%d", &T);
	while (T--){
		input();
		if (G.size() == 0){ puts("0"); continue; }
		build();
		printf("%d\n", F.Dinic(from, to));
	}
	return 0;
}
时间: 2024-10-23 17:13:51

CSU 1355 地雷清除计划 网格图清除最少的地雷使得对角联通 最小割的相关文章

图论-最短路-dijkstra / 0-1BFS-使网格图至少有一条有效路径的最小代价

2020-03-01 22:59:59 问题描述: 给你一个 m x n 的网格图 grid . grid 中每个格子都有一个数字,对应着从该格子出发下一步走的方向. grid[i][j] 中的数字可能为以下几种情况: 1 ,下一步往右走,也就是你会从 grid[i][j] 走到 grid[i][j + 1] 2 ,下一步往左走,也就是你会从 grid[i][j] 走到 grid[i][j - 1] 3 ,下一步往下走,也就是你会从 grid[i][j] 走到 grid[i + 1][j] 4 

【BZOJ1266】【AHOI2006】上学路线route 最短路建图转最小割

题解: 首先那个裸的单源最短路过程就过了吧. 然后说转的最小割. 就是我们考虑到从源点到汇点有多条最短路,我们需要切断一些边,使得所有的最短路都被切断. 首先这是个很裸的模型,切断?最小割! 如果你想不到,那不妨这么想: 我们切断所有最短路,那么每条最短路都有一个路径,上面有若干条边,那么我们需要至少切断其中的一部分. 而所有的局部最短路都满足一个性质: 就是从源点到某点的最短路长度固定(这个很显然吧,都"最"短了) 那么我们让所有的最短路径都被切断,就会使得一些在最短路径之一的边被切

图的全局最小割的Stoer-Wagner算法及例题

Stoer-Wagner算法基本思想:如果能求出图中某两个顶点之间的最小割,更新答案后合并这两个顶点继续求最小割,到最后就得到答案. 算法步骤: ------------------------------------------------------------------------------------------------------------------------- (1)首先初始化,设最小割ans = INF                                

关于清除丢失贴图与IES文件

fn YY_clrmessingmaps = ( YY_messingmap = #() allBitmaps = getClassInstances BitmapTexture -- 所有材质 for m=1 to allBitmaps.count do ( if not doesfileexist allBitmaps[m].filename do append YY_messingmap m ) -- 收集丢失贴图 if YY_messingmap.count != 0 do for c

Matlab-Octave中绘制网格图和等高线:mesh 和 surf

x=linspace(-50, 50, 50); % 在x轴上取50点y=linspace(-25, 25, 25); % 在y轴上取25点[xx,yy]=meshgrid(x, y); % xx和yy都是矩阵zz=8000-2.*xx.*xx-5.*yy.*yy; % 计算函数值,zz也是21x21的矩阵surf(xx, yy, zz); % 画出立体曲面图colorbar; %如下图,右边那个色卡 可按回车 contour(xx,yy,zz)colorbar 可按回车surfc(xx,yy,

HDU 3036 Escape 网格图多人逃生 网络流||二分匹配 建图技巧

前言 在编程过程中总结归纳出来的一种编程经验,从而形成的设计思想称为设计模式. 设计模式有23种.它适用于所有的编程语言. 常用的有创新型的设计模式:简单工厂.抽象工厂和单例模式:行为型的设计模式:模板设计模式.观察者模式和命令模式:结构性的设计模式:适配器设计模式.代理模式(静态和动态两种,典型的有在spring的AOP编程中使用)和装饰器设计模式. 正文 单例模式(singleton) 保证一个类在内存中只能创建一个实例. 1.实现步骤: 1)将构造器私有化,即使用private修饰构造器

[CF963E]Circles of Waiting[高斯消元网格图优化+期望]

题意 你初始位于 \((0,0)\) ,每次向上下左右四个方向走一步有确定的概率,问你什么时候可以位于圆心为 \((0,0)\) ,半径为 \(R\) 的圆. \(R\le 50\) 分析 暴力 \(O(R^6)\) 的高斯消元复杂度太高. 注意到本题在网格图上操作,假设我们从上至下从左至右依次给在圆内的点标号,那么对于当前点来说,相关的点(除了等式右边)和他的标号都不超过 \(2R\) .所以高斯消元的时候只需要考虑向下的 \(2R\) 行和向右的 \(2R\) 列即可. 以前写的消成单位矩阵

qbzt网格图路径问题——题解

题目内容: 给定一个长n高2的网格图,如下图: 有三种操作: 1.删除一条边 2.将一条删除的边再加回来 3.询问从一个点到另一个点的不同路径数(不可经过重复边) 算法复杂度要求不能大于 n log n (由于只有题面,没有输入格式和数据,所以在此只讲一下思路) 正解是用线段树维护. 先给网格图标上号: 对于线段树上的节点,若它维护的区间为[l,r],则它就对应一个l~r的网格图G: a1.a2.a3.a4分别代表G的左上.右上.左下.右下方的点.同时有布尔变量b1~b6 ,bi表示ai连向外面

[luoguP2762] 太空飞行计划问题(最大权闭合图—最小割—最大流)

传送门 如果将每一个实验和其所对的仪器连一条有向边,那么原图就是一个dag图(有向无环) 每一个点都有一个点权,实验为收益(正数),仪器为花费(负数). 那么接下来可以引出闭合图的概念了. 闭合图是原图的一个点集,其中这个点集中每个点的出边所指向的点依然在这个点集中,那么这个点集就是个闭合图. 比如论文中的这个图: 在图 3.1 中的网络有 9 个闭合图(含空集):∅,{3,4,5},{4,5},{5},{2,4,5},{2,5},{2,3,4,5},{1,2,4,5},{1,2,3,4,5}