CCF 201312-5 I'm stuck!
問題
問題描述
給定一個R行C列的地圖,地圖的每一個方格可能是'#', '+', '-', '|', '.', 'S', 'T'七個字元中的一個,分別表示如下意思:
'#': 任何時候玩家都不能移動到此方格;
'+': 當玩家到達這一方格後,下一步可以向上下左右四個方向相鄰的任意一個非'#'方格移動一格;
'-': 當玩家到達這一方格後,下一步可以向左右兩個方向相鄰的一個非'#'方格移動一格;
'|': 當玩家到達這一方格後,下一步可以向上下兩個方向相鄰的一個非'#'方格移動一格;
'.': 當玩家到達這一方格後,下一步只能向下移動一格。如果下面相鄰的方格為'#',則玩家不能再移動;
'S': 玩家的初始位置,地圖中只會有一個初始位置。玩家到達這一方格後,下一步可以向上下左右四個方向相鄰的任意一個非'#'方格移動一格;
'T': 玩家的目標位置,地圖中只會有一個目標位置。玩家到達這一方格後,可以選擇完成任務,也可以選擇不完成任務繼續移動。如果繼續移動下一步可以向上下左右四個方向相鄰的任意一個非'#'方格移動一格。
此外,玩家不能移動出地圖。
請找出滿足下面兩個性質的方格個數:
1. 玩家可以從初始位置移動到此方格;
2. 玩家不可以從此方格移動到目標位置。
輸入格式
輸入的第一行包括兩個整數R 和C,分別表示地圖的行和列數。(1 ≤ R, C ≤ 50)。
接下來的R行每行都包含C個字元。它們表示地圖的格子。地圖上恰好有一個'S'和一個'T'。
輸出格式
如果玩家在初始位置就已經不能到達終點了,就輸出“I'm stuck!”(不含雙引號)。否則的話,輸出滿足性質的方格的個數。
樣例輸入
5 5
--+-+
..|#.
..|##
S-+-T
####.
樣例輸出
2
樣例說明
如果把滿足性質的方格在地圖上用'X'標記出來的話,地圖如下所示:
--+-+
..|#X
..|##
S-+-T
####X
問題分析
性質1顯而易見,使用DFS(BFS也行)即可找到從起點S出發能到達的所有點,將能到達的點記錄下來:s_arrived[x][y] = true;
問題的難點在於性質2,暴力的做法可以遍歷所有S可以到達的點({(x,y)|s_arrived[x][y] = true}),對每個點進行DFS(BFS也行)看其能否到達終點T並計數。但這樣時間複雜度較高,下面給出另一種方法。
既然要找到不能到達T的點,那麼我們可以先找能到達T的點並記錄下來:t_arrived[x][y] = true;那麼,什麼點能到達T呢?便於理解,舉例如下(A、B、C都是點,例子中不關心其座標):
存在一條路徑:S-->A-->B-->C-->T
第一步,我們可以先找C,顯然它與T相鄰(啟發:第一步找與T相鄰的點);第二步,找能到達C的點,顯然它與C相鄰;從這2步可以看出貌似可以遞迴地來找到所有能到達T的點,再深入想就會發現遞迴實現其實就是從T開始進行DFS,具體細節描述如下(為了敘述簡便,把能到達X的點統稱為destination(X)):
1.找到所有與X相鄰的點,顯然他們都是destination(X)的候選者;
2.檢查這些候選者能否到達X,去掉那些不能到達X的候選者,剩下的就是可以到達X的點,即destination(X)。使用題中給出的樣例作為例子:
T左邊的(4,4)和T下邊的(5,5)都可以作為X(令X=T)的destination的候選者,我們只需去掉那些不能到X的候選者,如(5,5),因為它是'.',所以它只能向下,不能到達T。
3.destination(T)、destination( destination(T) )、destination( destination( destination(T) ) )……即為所有能到達T的點。
求解過程中需要的量:X、與X相鄰的候選者
候選者篩選規則:若候選者為‘-’,則需保證X與候選者在同一行;若候選者為‘|’,則需保證X與候選者在同一列;若候選者為‘.’,則需保證X在候選者的下方。
最後,符合性質1和性質2的點為:{(x,y)|s_arrived[x][y] = true && t_arrived[x][y] = false}.
程式碼
#include<cstdio>
#include<cstdlib>
#include<iostream>
using namespace std;
const int N = 50;
char map[N+1][N+1];
bool ban[N+1][N+1] = {false};
bool s_arrived[N+1][N+1] = {false};
bool t_arrived[N+1][N+1] = {false};
int r,c;
const int plus_sign[][2] = {{-1,0},{0,1},{1,0},{0,-1}};
const int minus_sign[][2] = {{0,-1},{0,1}};
const int stick_sign[][2] = {{-1,0},{1,0}};
const int dot_sign[][2] = {{1,0}};
bool IsLegal(int x,int y){
if(x<1||x>r||y<1||y>c)
return false;
return true;
}
//int test_s = 0;
//int test_t = 0;
void dfs_s(int x,int y){
if(!IsLegal(x,y)||ban[x][y]||s_arrived[x][y])
return;
s_arrived[x][y] = true;
// for(int i=0;i<test_s;i++)
// printf("\t");
// test_s++;
// printf("dfs_s(%d,%d)\n",x,y);
switch(map[x][y]){
case '+':
for(int i=0;i<4;i++)
dfs_s(x+plus_sign[i][0],y+plus_sign[i][1]);
break;
case '-':
for(int i=0;i<2;i++)
dfs_s(x+minus_sign[i][0],y+minus_sign[i][1]);
break;
case '|':
for(int i=0;i<2;i++)
dfs_s(x+stick_sign[i][0],y+stick_sign[i][1]);
break;
case '.':
dfs_s(x+dot_sign[0][0],y+dot_sign[0][1]);
break;
case 'S':
for(int i=0;i<4;i++)
dfs_s(x+plus_sign[i][0],y+plus_sign[i][1]);
break;
case 'T':
for(int i=0;i<4;i++)
dfs_s(x+plus_sign[i][0],y+plus_sign[i][1]);
break;
default:
printf("error!\n");
}
// test_s--;
}
void dfs_t(int x,int y,int last_x,int last_y){//last_x,last_y記錄前一層(dfs)的點的x,y,用於分析反向情況
if(!IsLegal(x,y)||ban[x][y]||t_arrived[x][y])
return;
// for(int i=0;i<test_t;i++)
// printf("\t");
// test_t++;
// printf("dfs_t(%d,%d)\n",x,y);
if(map[x][y]=='-'&&last_x!=x)//保證同一行(不在同一行就return)
return;
else if(map[x][y]=='|'&&last_y!=y)//保證同一列(不在同一列就return)
return;
else if(map[x][y]=='.'&&last_x!=x+1)//保證last在x的下方(否則就return)
return;
t_arrived[x][y] = true;
for(int i=0;i<4;i++)
dfs_t(x+plus_sign[i][0],y+plus_sign[i][1],x,y);
// test_t--;
}
int main(){
int s_x,s_y,t_x,t_y,cnt = 0;
scanf("%d %d",&r,&c);
for(int i=1;i<=r;i++){
getchar();
for(int j=1;j<=c;j++){
scanf("%c",&map[i][j]);
if(map[i][j]=='#')
ban[i][j] = true;
else if(map[i][j]=='S'){
s_x = i;
s_y = j;
}
else if(map[i][j]=='T'){
t_x = i;
t_y = j;
}
}
}
//先從S開始找到所有能到達的點,結果存在s_arrived中
dfs_s(s_x,s_y);
if(!s_arrived[t_x][t_y]){
printf("I'm stuck!\n");
return 0;
}
dfs_t(t_x,t_y,t_x,t_y);
for(int i=1;i<=r;i++)
for(int j=1;j<=c;j++)
if(s_arrived[i][j]&&!t_arrived[i][j])
cnt++;
printf("%d\n",cnt);
return 0;
}
注意事項
1、程式碼的註釋部分完全可以忽略,如果加上可以檢視DFS的具體過程。
2、plus_sign[][2]、minus_sign[][2]、stick_sign[][2]和dot_sign[][2]這四個二維陣列對應'+'、'-'、'|'、'.' 四種情況,用來確定允許進一步搜尋的方向,完全可以使用if-else語句替換。
3、dfs_t的後2個引數記錄的是問題分析中X的座標。
有疑問歡迎提出!
相關文章
- I'm coming...
- I'm Hello World
- Deep Learn I'm back.
- I'm shocked that they chose for the City to be the central hub
- How to Cancel a Concurrent Request Stuck in the Queue?
- CCF ISBN
- CCF NLP比賽
- 題解:P9957 [USACO20DEC] Stuck in a Rut B
- 2023 Hubei Provincial Collegiate Programming Contest題解 C F H I J K M
- ICPC2023南京站題解(A C D F G I L M)
- S01E01-Buddy, Why do you keep getting stuck like this
- ICPC2023瀋陽站題解(B C D E I J K M)
- 高效能 低功耗Cortex-A53核心板 | i.MX8M Mini
- 基於飛凌i.MX8M Mini核心板的加油機方案
- ccf省賽3等獎
- ccf碰撞的小球(100分)
- CCF-CSP 20180302:小球碰撞
- CCF 201409-2 畫圖
- i = ++i
- 你真的瞭解 i++, ++i 和 i+++++i 以及 i+++i++ 嗎?
- CCF CSP202006-2 稀疏向量
- CCF-回收站選址
- CCF第二題之視窗
- CCF跳一跳Python程式Python
- PHP中關於date("Y-m-d H:i:s")慢8小時的解決PHP
- ccf 公共鑰匙盒 java實現Java
- CCF 201412-2 Z字型掃描
- CCF 202009-2 風險人群篩查
- CCF 202006-2 稀疏向量【map的使用】
- CCF CSP 201809-1 賣菜 題解
- CCF 201509-4 高速公路(100分)
- CCF之網路延時(樹形dp)
- 搭載恩智浦i.MX 8M Plus處理器的核心板,它來了!
- i52430m裝win10系統重啟電腦進入會黑屏修復方法Win10
- Day13 備戰CCF-CSP練習
- Day7 備戰CCF-CSP練習
- Day4 備戰CCF-CSP練習
- CCF CSP-S 2024 提高組初賽解析