# **Zerojudge d406 倒水時間**
:::warning
題目:
水由上而下的流,現在給你水管之間的地圖
水可以由上而下,可以往右流,往左流。
可是呢 ... 有時候也可以往上流
現在從地圖的最上方開始倒水,請輸出到的時間,水不能走的地方輸出0。
※ 開始倒的地方只有 1 個且只在第一列倒。
每組測資的第一列有一個數字 S,若 S = 2 代表水不能往上流, S = 1 代表水可以往上流。
第二列有兩個數字 N M, N 代表接下來有 N 列,M代表每列上有多少數字。( 1≦ N M ≦ 100 )
接下來會有 N 列,每列上有 M 個數字, 1 代表有水管, 0 則代表沒有。
https://zerojudge.tw/ShowProblem?problemid=d406
:::
這題是經典的 ==BFS(廣度優先搜尋法)的題目==,但是多加了不能向上的限制
***
首先引入bfs必要的queue、pair陣列的標頭檔
```cpp=
#include<iostream>
#include<queue> //queue
#include<utility> //pair
```
***
宣告題目需要的變數(全域變數,部分變數用意之後會提)
```cpp=
int first_1, s, n, m, arr[1000][1000], ans[1000][1000], dx[4] = {1,-1,0,0}, dy[4] = {0,0,1,-1};
bool visited[1000][1000] = {0};
```
***
輸入
```cpp=
cin >> s;
cin >> n >> m;
for(int i=0 ; i<n ; i++)
for(int j=0 ; j<m ; j++)
{
cin >> arr[i][j]; //輸入
if(i == 0 && arr[i][j] == 1) //找到起點
first_1 = j;
}
```
因為題目說 ==開始倒的地方只有 1 個且只在第一列倒==,所以我們在輸入第一列時( i=0 )便找出倒水的起點( j )
***
輸入完開始bfs,我習慣把bfs寫成函式,這也是為什麼我是宣告全域變數的原因,如果是在int main宣告那這樣只是**區域變數**,int main外的函式無法使用
```cpp=
bfs(); //呼叫函式
```
```cpp=
void bfs()
{
queue<pair<int,int>> q; //首先宣告一個用pair的queue陣列
visited[0][first_1] = 1; //visited記錄這個點有沒有被走過(有就設為1)
ans[0][first_1] = 1;//(ans陣列用來紀錄時間點)
q.push(make_pair(0,first_1));//把0,first也就是起點丟進queue陣列裡
while(q.size()!=0)
{
pair<int,int> p = q.front();
q.pop();
for(int i=0 ; i<4 ; i++)
{
if(s == 2 && i == 1) continue; //x+dx[1] 為向上走故跳過
int nx = p.first + dx[i], ny = p.second + dy[i];
if(nx < 0||ny < 0||nx >= n||ny >= m) continue;
if(arr[nx][ny] == 1 && visited[nx][ny] == 0) //若這個點為1(水管)且沒被走過
{
q.push(make_pair(nx,ny));//將這個點丟進queue陣列
visited[nx][ny] = 1;//將這個點設定為已走過
ans[nx][ny] = ans[p.first][p.second] + 1;
}
}
}
return;
}
```
用**pair**可以方便存取
而dx[4] = {1,-1,0,0}, dy[4] = {0,0,1,-1}是變量
可以直接用for迴圈重複4次就可以有上下左右4個點了
至於22行的部分為何是**ans[nx][ny] = ans[p.first][p.second] + 1**,==**因為現在這一格是前一格的下一個時間點所以加1**== (起點為1,下一個為水管且還未被走過就是2,以此類推),這樣最後輸出就可以知道每格的時間點了
***
輸出
```cpp=
for(int i=0 ; i<n ; i++)
{
if(i>0) cout << "\n";//換行用
for(int j=0 ; j<m ; j++)
j < m-1 ? cout << ans[i][j] << " " : cout << ans[i][j];
}
```
第五行是讓輸出時每行結尾沒有空格
題目有說**水不能走的地方輸出0**,但因為我們的 ==ans陣列是全域變數即**預設為0**==,所以牆壁和走不到的地方不會被動到,也就是0了
***
最後把重複輸入的條件加上去就好了
:::success
完整程式碼
:::
```cpp=
#include<iostream>
#include<queue>
#include<utility>
using namespace std;
int Case = 1, first_1, s, n, m, arr[1000][1000], ans[1000][1000], dx[4] = {1,-1,0,0}, dy[4] = {0,0,1,-1};
bool visited[1000][1000] = {0};
void bfs()
{
queue<pair<int,int>> q;
visited[0][first_1] = 1;
ans[0][first_1] = 1;
q.push(make_pair(0,first_1));
while(q.size()!=0)
{
pair<int,int> p = q.front();
q.pop();
for(int i=0 ; i<4 ; i++)
{
if(s == 2 && i == 1) continue;
int nx = p.first + dx[i], ny = p.second + dy[i];
if(nx < 0||ny < 0||nx >= n||ny >= m) continue;
if(arr[nx][ny] == 1 && visited[nx][ny] == 0)
{
q.push(make_pair(nx,ny));
visited[nx][ny] = 1;
ans[nx][ny] = ans[p.first][p.second] + 1;
}
}
}
return;
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0);
while(cin>>s)
{
cin >> n >> m;
for(int i=0 ; i<n ; i++)
for(int j=0 ; j<m ; j++)
{
cin >> arr[i][j];
if(i == 0 && arr[i][j] == 1)
first_1 = j;
}
bfs();
cout << "Case " << Case << ":\n";
for(int i=0 ; i<n ; i++)
{
if(i>0) cout << "\n";
for(int j=0 ; j<m ; j++)
j < m-1 ? cout << ans[i][j] << " " : cout << ans[i][j];
}
cout << "\n";
Case++;
for(int i = 0 ; i < 1000 ; i++)
for(int j = 0 ; j < 1000 ; j++)
{
arr[i][j] = 0;
ans[i][j] = 0;
visited[i][j] = 0;
}
}
return 0;
}
```
:::success
掰掰 :D
:::