Codeforces Round #583 D. Treasure Island

1 篇文章 0 订阅

D. Treasure Island

题目链接: http://codeforces.com/contest/1214/problem/D

Problem Description

All of us love treasures, right? That’s why young Vasya is heading for a Treasure Island.
Treasure Island may be represented as a rectangular table n × m n×m n×m which is surrounded by the ocean. Let us number rows of the field with consecutive integers from 1 1 1 to n n n from top to bottom and columns with consecutive integers from 1 1 1 to m m m from left to right. Denote the cell in r-th row and c c c-th column as ( r , c ) (r,c) (r,c). Some of the island cells contain impassable forests, and some cells are free and passable. Treasure is hidden in cell ( n , m ) (n,m) (n,m).
Vasya got off the ship in cell ( 1 , 1 ) (1,1) (1,1). Now he wants to reach the treasure. He is hurrying up, so he can move only from cell to the cell in next row (downwards) or next column (rightwards), i.e. from cell ( x , y ) (x,y) (x,y) he can move only to cells ( x + 1 , y ) (x+1,y) (x+1,y) and ( x , y + 1 ) (x,y+1) (x,y+1). Of course Vasya can’t move through cells with impassable forests.
Evil Witch is aware of Vasya’s journey and she is going to prevent him from reaching the treasure. Before Vasya’s first move she is able to grow using her evil magic impassable forests in previously free cells. Witch is able to grow a forest in any number of any free cells except cells ( 1 , 1 ) (1,1) (1,1) where Vasya got off his ship and ( n , m ) (n,m) (n,m) where the treasure is hidden.
Help Evil Witch by finding out the minimum number of cells she has to turn into impassable forests so that Vasya is no longer able to reach the treasure.

Input

First line of input contains two positive integers n , m ( 3 ≤ n ⋅ m ≤ 1000000 ) n, m (3≤n⋅m≤1000000) n,m(3nm1000000), sizes of the island.
Following n n n lines contains strings s i s_i si of length m m m describing the island, j j j-th character of string s i s_i si equals “#” if cell ( i , j ) (i,j) (i,j) contains an impassable forest and “.” if the cell is free and passable. Let us remind you that Vasya gets of his ship at the cell ( 1 , 1 ) (1,1) (1,1), i.e. the first cell of the first row, and he wants to reach cell ( n , m ) (n,m) (n,m), i.e. the last cell of the last row.
It’s guaranteed, that cells ( 1 , 1 ) (1,1) (1,1) and ( n , m ) (n,m) (n,m) are empty.

Output

Print the only integer k k k, which is the minimum number of cells Evil Witch has to turn into impassable forest in order to prevent Vasya from reaching the treasure.

Sample Input

2 2

Sample Output

2

题意

给定一个 n ∗ m n*m nm的矩阵,“.”代表可以通过,“#”代表不能通过,你可以使得任意数量的“.”变为“#”。
现在要求改变最少数量的“.”,使得不存在路径从 ( 1 , 1 ) (1,1) (1,1)可以走到 ( n , m ) (n,m) (n,m)。输入保证 ( 1 , 1 ) (1,1) (1,1) ( n , m ) (n,m) (n,m)必为“.”。

思路

其实答案就是在0,1和2中选择。
a n s = 0    ⟹    ans=0 \implies ans=0 ( 1 , 1 ) (1,1) (1,1) ( n , m ) (n,m) (n,m)无路径。
a n s = 1    ⟹    ans=1 \implies ans=1 ( 1 , 1 ) (1,1) (1,1) ( n , m ) (n,m) (n,m)的所有路径有一个必经点 ( x , y ) (x,y) (x,y)
a n s = 2    ⟹    ans=2 \implies ans=2 ( 1 , 1 ) (1,1) (1,1)或者 ( n , m ) (n,m) (n,m)封闭起来。
其实感觉 d f s dfs dfs写的会舒服一点,但赛后看见有人说最小割的时候就感觉最小割确实可以,所以用了最小割莽了一发。(板子不够优秀,仅供参考, 935 m s 935ms 935ms极限飘过,经过测试队友板子可以达到 327 m s 327ms 327ms
在一开始只是普通的建边,但是碰到下面这组样例时就会wa
3 3
..#
...
#..
上面这组样例跑出来的最小割是2,但是其实只需要改变一个点就好了,这就想到应该限制对于一个点需要限制这个点流过的流量最大为1,然后想了一天也没想出怎么处理这个情况(学艺不精),在晚上和队友沟通之后发现可以利用拆点来限制流过一个点的流量(瞬间被点醒)。
以上面的样例的 ( 2 , 2 ) (2,2) (2,2)为例,可以先选出应该虚拟节点 ( x , y ) (x, y) (x,y),在这两点之间建一条流量为1的边,然后再从 ( x , y ) (x,y) (x,y)各建一条到 ( 3 , 2 ) (3,2) (3,2) ( 2 , 3 ) (2,3) (2,3)流量为1的边,这样就限制了经过 ( 2 , 2 ) (2,2) (2,2)的流量。
在最后还wa了一发,在起点的时候没有经过起点流量为1的限制,需要特判一下。

代码

#include <bits/stdc++.h>
#define pi acos(-1.0)
#define ll long long
#define ull unsigned long long
#define esp 1e-9
#define inf 0x3f3f3f3f
#define inff 0x3f3f3f3f3f3f3f3f
#define Pair pair<ll, ll>
#define It list<node>::iterator
   
using namespace std;
 
const int N = 2e6+5;//考虑上虚拟节点,大小设置为2*n*m<=2e6
 
 //以下为最大流模板(复杂度不够优秀,不要在意)
struct node{//弧 
	int from, to, cap, flow;//cap容量,flow流量
};
 
struct Dinic{
	int n, m, s, t;
	bool vis[N];//BFS使用
	int dis[N], cur[N];//起点到i的距离,当前弧的下标 
	vector<int> G[N];
	vector<node> edge;
	
	void add(int from, int to, int cap){//加边 
		edge.push_back((node){from, to, cap, 0});
		edge.push_back((node){to, from, 0, 0});
		int sz = edge.size();
		G[from].push_back(sz-2);
		G[to].push_back(sz-1);
	}
	
	bool bfs(){
		queue<ll> que;
		dis[s] = 0;
		memset(vis, 0, sizeof(vis));
		que.push(s); vis[s] = 1;
		while (que.size()){
			int u = que.front(); que.pop();
			for (int i = 0; i < G[u].size(); i++){
				node &e = edge[G[u][i]];
				if (!vis[e.to] && e.cap>e.flow){
					que.push(e.to); vis[e.to] = 1;
					dis[e.to] = dis[u]+1;
				}
			}
		}
		return vis[t];
	}
	
	int dfs(int pos, int f){
		if (pos==t || f==0){
			return f;
		}
		else{
			int d, flow = 0;
			for (int &i = cur[pos]; i < G[pos].size(); i++){
				node &e = edge[G[pos][i]];
				if (dis[pos]+1==dis[e.to] && (d=dfs(e.to, min(f, e.cap-e.flow)))>0){
					e.flow += d;
					edge[G[pos][i]^1].flow -= d;
					flow += d;
					f -= d;
					if (f == 0){
						break;
					}
				}
			}
			return flow;
		}
	}
	
	int max_flow(int s, int t){
		int flow = 0;
		this->s = s; this->t = t;
		while (bfs()){
			memset(cur, 0, sizeof(cur));
			flow += dfs(s, inf);
		}
		return flow;
	}
};
 
char ch;
Dinic solve;
vector<char> G[N];
int n, m, s, t, u, v, cap;
int dir[2][2] = {1, 0, 0, 1};
 
int main() {
	scanf("%d%d", &n, &m);
	getchar();
	s = 0; t = n*m-1;//设置起点终点
	//读图
	for (int i = 0; i < n; i++){
		for (int j = 0; j < m; j++){
			scanf("%c", &ch);
			G[i].push_back(ch);
		}
		getchar();
	}
	for (int i = 0; i < n; i++){
		for (int j = 0; j < m; j++){
			if (G[i][j] == '.'){
				if(i==0&&j==0){//特判起点
					for (int k = 0; k < 2; k++){
						int x = i+dir[k][0], y = j+dir[k][1];
						if (0<=x && x<n && 0<=y && y<m && G[x][y]=='.'){
							solve.add(i*m+j, x*m+y, 1);
						}
					}
				}
				else{
					solve.add(i*m+j, n*m+i*m+j, 1);//限制点上通过的流量不超过1
					for (int k = 0; k < 2; k++){
						int x = i+dir[k][0], y = j+dir[k][1];
						if (0<=x && x<n && 0<=y && y<m && G[x][y]=='.'){
							solve.add(n*m+i*m+j, x*m+y, 1);//从虚拟节点建边
						}
					}
				}
				
			}
		}
	}
	printf("%d\n", solve.max_flow(s, t));//最小割(最大流)即是答案
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值