[Codeforces 1214D]Treasure Island(dfs)
題面
給出一個n*m的字符矩陣,'.'表示能通過,'#'表示不能通過。每步可以往下或往右走。問至少把多少個'.'變成'#',才能讓從(1,1)出發不能到達(n,m)
\(n \times m \leq 10^6\)
分析
第一眼還以為是最小割,一看數據范圍感覺會TLE。實際上搜索就可以了。
首先發現答案肯定是0,1,2中的一個(輸出rand()%3),因為最多用2個'#',把(1,1)右側和下方堵住就可以了。
然后注意到若存在一個點滿足所有(1,1)到(n,m)的路徑都經過那個點,那答案就為1.這實際上是一個有向圖求割點的問題,類似HDU3313.
我們只需要dfs一遍,找出任意一條(1,1)到(n,m)的路徑,然后把路徑上的點標記為1。注意若(1,1)不能到(n,m),直接輸出0.第二次dfs保證不經過路徑上的點。如果第二次的時候(1,1)不能到(n,m),那就說明存在割點,答案為1。否則答案為2.
注意到數據范圍\(n \times m \leq 10^6\),沒有給出n,m的范圍,所以要用vector存儲字符矩陣。
代碼
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define maxn 1000000
using namespace std;
int n,m;
char in[maxn+5];
vector<char>s[maxn+5];
struct node{
int x;
int y;
node(){
}
node(int _x,int _y){
x=_x;
y=_y;
}
};
const int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
vector<bool>vis[maxn+5];
vector<bool>mark[maxn+5];
bool dfs(int x,int y){
if(x>n||y>m) return 0;
if(x==n&&y==m) return 1;
if(s[x][y]=='#'||vis[x][y]||mark[x][y]) return 0;
vis[x][y]=1;
bool flag=dfs(x+1,y);
if(!flag) flag=dfs(x,y+1);
//優先往(x+1,y)走,如果走不通再往(x,y+1)走,這樣就可以避免被堵住的情況,詳見評論區
if(flag) mark[x][y]=1;
return flag;
}
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%s",in+1);
s[i].resize(m+1);
for(int j=1;j<=m;j++) s[i][j]=in[j];
vis[i].resize(m+1);
mark[i].resize(m+1);
}
bool flag=dfs(1,1);
if(!flag) printf("0\n");
else{
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++) vis[i][j]=0;
}
mark[1][1]=mark[n][m]=0;
flag=dfs(1,1);
if(!flag) printf("1\n");
else printf("2\n");
}
}