## 最後一個了 二維陣列走訪 二維陣列一般使用巢狀for迴圈走訪每個元素。 ### 輸入 假設要讀入 m 列 n 行的矩陣: ```cpp= int m, n; cin >> m >> n; int a[m][n]; for(int i=0; i<m; i++) for (int j=0; j<n; j++) cin >> a[i][j]; ``` 注意外圈的註標放在一維,內圈的註標放二維。 ### 輸出 如果要求每列元素列印時間隔一個空白字元,且最後一個元素後面沒有空白字元,作法如下: ```cpp= for (int i=0; i<m; i++) { for (int j=0; j<n; j++) { if (j==0) cout << a[i][j]; else cout << " " << a[i][j]; } cout << "\n"; } ``` 注意外圈要負責列印每列的換行符號。 ### 鄰居走訪 假設陣列元素座標為 (x,y),它的鄰居如下圖所示: ![image.png](https://hackmd.io/_uploads/rkwCZu8QT.png =50%x) 由圖可知,上下左右4個鄰居(底色為淡橘色)的座標: - 上 (x-1, y) - 下 (x+1, y) - 左 (x, y-1) - 右 (x, y+1) 假設目前位置是 (x, y),各鄰居的位移量為 (a, b),各鄰居的位置即為 (x+a, y+b)。鄰居位移量: - 上鄰居的位移量是(-1,0) - 下鄰居的位移量是(1, 0) - 左鄰居的位移量是(0,-1) - 右鄰居的位移量是(0, 1) 可以用一個二維陣列(方法一)或兩個一維陣列(方法二)來枚舉鄰居。 方法1: ```cpp= const int mov[4][2] = {{-1,0}, {1,0}, {0,-1}, {0,1}}; int x, y, nx, ny; for (x=0; x<m; x++) { for (y=0; y<n; y++) { for (int i=0; i<4; i++) { int nx = x+mov[i][0]; int ny = y+mov[i][1]; // 位置不合法跳過 if (nx<0 || nx>=m || ny<0 || ny>=n) continue; // 處理 a[nx][ny] // ... // ... } } } ``` 上面的const是把變數值鎖住,讓它不能變更或修改 第9行是座標不合法就跳過不處理。 下面的程式是不給第一個陣列大小的寫法,真的可以這樣寫,但第二個陣列一定要有值: ```cpp= const int mov[][2] = {{-1,0}, {1,0}, {0,-1}, {0,1}}; ``` 枚舉上、下、左、右、左上、右上、左下、右下 觀察到對角線鄰居(上圖底色為淺綠)的位移量規則為$(\pm1, \pm1)$,可知8鄰居位移量的二維陣列寫法: ```cpp= const int mov[][2]={{0,1},{0,-1},{1,0},{-1,0},{-1,-1},{-1,1},{1,-1},{1,1}}; ``` :::success 走訪位移量的可能(我掰的) $(0,\pm1), (\pm1,0), (\pm1,\pm1)$ ::: ## 炸彈偵測器 題目:[f149. 炸彈偵測器](https://zerojudge.tw/ShowProblem?problemid=f149) :::spoiler 參考程式 ```cpp= #include <bits/stdc++.h> using namespace std; int main() { int R,C; cin >> R >> C; int a[R][C]; int all_bomb=0, remove_bomb=0; for (int i=0; i<R; i++) { for (int j=0; j<C; j++) { cin >> a[i][j]; if (a[i][j]==1) all_bomb++; } } const int mov[][2] = {{0,1},{0,-1},{1,0},{-1,0},{-1,-1},{1,1},{1,-1},{-1,1}}; int x,y,nx,ny; for (x=0; x<R; x++) { for (y=0; y<C; y++) { // 如果a[x][y]不是5就跳過 if (a[x][y]!=5) continue; // a[x][y]為偵測器 bool again=false; // 8個鄰居有偵測器,要設為true // 檢查8個鄰居是否有5(偵測器) for (int i=0; i<8; i++) { nx = x+mov[i][0]; ny = y+mov[i][1]; if (nx>=0 && nx<R && ny>=0 && ny<C) { if (a[nx][ny]==5) { again=true; break; } } } if (again) continue; // 搜尋可移除的炸彈 // 再次搜尋8個鄰居是否有1 for (int i=0; i<8; i++) { nx = x+mov[i][0]; ny = y+mov[i][1]; if (nx>=0 && nx<R && ny>=0 && ny<C) { if (a[nx][ny]==1) { a[nx][ny]=0; remove_bomb++; } } } } } cout << remove_bomb << " " << all_bomb-remove_bomb; } ``` ::: ## Box of Bricks 題目:[c067. Box of Bricks](https://zerojudge.tw/ShowProblem?problemid=c067) 想法:最小搬動數等於每堆積木數與平均積木數的差值絕對值總和的一半。 :::spoiler 參考程式 ```cpp= #include <bits/stdc++.h> using namespace std; int main() { int n; int cases=1; while(cin>>n) { if (n==0) break; int a[n]; int total=0; // 積木總數 for(int i=0; i<n; i++) { cin>>a[i]; total+=a[i]; } // 計算高度 int h = total/n; // 計算積木搬動個數 int mov=0; for (int i=0; i<n; i++) { mov+=abs(a[i]-h); } mov/=2; cout << "Set #" << cases << "\n"; cout << "The minimum number of moves is " << mov << ".\n\n"; cases++; } return 0; } ``` ::: ## 特殊位置 題目:[k732. 特殊位置](https://zerojudge.tw/ShowProblem?problemid=k732) :::spoiler 參考程式 ```cpp= int main() { ios_base::sync_with_stdio(0), cin.tie(0); int n,m; cin>>n>>m; int a[n][m]; vector<pair<int,int>> v; // 儲存特殊位置 for (int i=0; i<n; i++) for (int j=0; j<m; j++) cin>>a[i][j]; for (int i=0; i<n; i++) { for (int j=0; j<m; j++) { int x=a[i][j]; // 找到曼哈頓距離小於等於x座標,計算點數和 int sum=0; for (int ii=0; ii<n; ii++) { for (int jj=0; jj<m; jj++) { if (abs(ii-i)+abs(jj-j)<=x) sum+=a[ii][jj]; } } if (sum%10==x) v.push_back({i,j}); } } cout << v.size() << endl; for(auto& x:v) cout << x.first << " " << x.second << endl; return 0; } ``` ::: ## 賓果遊戲 題目:[b548. 賓果遊戲](https://zerojudge.tw/ShowProblem?problemid=b548) :::spoiler sol ```cpp= #include <bits/stdc++.h> using namespace std; int main() { int a[5][5]; for (int i = 0; i < 5; i++) for (int j = 0; j < 5; j++) cin >> a[i][j]; int num; while (cin >> num && num!=-1) for (int i = 0; i < 5; i++) for (int j = 0; j < 5; j++) if (num == a[i][j]) a[i][j] = 0; vector<int> b(26); // index i 的陣列值表示賓果數字 i 的得分 // 檢查每列會得分的數字 int cnt; // 記錄已經消除數字的數量 for (int i = 0; i < 5; i++) { cnt = 0; for (int j = 0; j < 5; j++) { if (a[i][j] == 0) cnt++; } if (cnt == 4) { for (int k = 0; k < 5; k++) { num=a[i][k]; if (num != 0) { b[num]++; } } } } // 檢查每行會得分的數字 for (int j = 0; j < 5; j++) { cnt = 0; for (int i = 0; i < 5; i++) { if (a[i][j] == 0) cnt++; } if (cnt == 4) { for (int k = 0; k < 5; k++) { num = a[k][j]; if (num != 0) { b[num]++; } } } } // 右下左上對角線檢查 cnt = 0; for(int i = 0; i < 5; i++) { if (a[i][i]==0) cnt++; } if(cnt == 4) { for(int i = 0; i < 5; i++) { num = a[i][i]; if(num != 0) { b[num]++; } } } // 右上左下對角線檢查 cnt = 0; for (int i = 0, j = 4; i < 5; i++,j--) { if(a[i][j]==0) cnt++; } if (cnt == 4) { for(int i = 0, j = 4; i < 5; i++, j--) { num = a[i][j]; if(num != 0) { b[num]++; } } } int mx = -1; for(int i = 1; i<=25; i++) mx = max(mx, b[i]); for(int i = 1; i<=25; i++) { if(b[i]==mx) { cout << i; break; } } return 0; }