題目傳送門
說明
“哞林匹克”運動會中的一大亮點就是滑雪比賽。滑雪比賽的場地是一個 \(n \times m\) 的矩陣(\(1<=n,m<=500\)),每一個點有對應的海拔(海拔均在 \(0\) 到 \(10^9\) 的範圍內)。
主機板方指定了若干個點作為路標。同時,主辦方還要規定一個比賽係數 \(d\) ,這個係數規定選手不能在海拔高度差大於 \(d\) 的兩個點之間活動。在規則允許的範圍內,選手可以向東南西北任意一個點活動,選手的任務是在儘量短的時間內到達所有的路標打卡。
為了確保每一個選手的安全,主辦方想將係數d設定得越小越好,然而這個係數要確保每一個路標是相互聯通的(否則比賽就失去了意義)。
輸入格式
第一行輸入兩個整數n和m,表示比賽場地的大小;
接下來輸入兩個 \(n \times m\) 的矩陣,第一個表示每一個點的海拔高度,第二個矩陣表示路標的設定情況,其中數字 \(1\) 表示該點為路標,數字 \(0\) 表示不是路標。
輸出格式
輸出 \(d\)。
樣例
輸入資料 \(1\)
3 5
20 21 18 99 5
19 22 20 16 26
18 17 40 60 80
1 0 0 0 1
0 0 0 0 0
0 0 0 0 1
輸出資料 \(1\)
21
-------------------------------------------------------
分析
本題每次列舉 \(d\)。每次 BFS 遍歷一遍地圖,如果發現一個點到另一個點距離大於 \(d\) 的話,則不走這條路。否則把這個新的點加入佇列。
不過本題的 \(d\) 高達 \(10^9\),所以暴力列舉肯定是不行的,因此,我們就要用上二分答案來解決這道題了。
程式碼
#include <bits/stdc++.h>
#define int long long //開個long long,以防萬一
using namespace std;
int n, m, l = 0, r = 1e9, ans = INT_MAX, map1[510][510], map2[510][510]; //map1表示地圖,map2表示打卡點
int dx[4] = {0, 1, 0, -1}; //方位陣列
int dy[4] = {1, 0, -1, 0};
bool vis[510][510];
struct node {
int x, y; //記錄一個座標
};
bool check(int d) {
memset(vis, 0, sizeof(vis)); //更新陣列
vis[1][1] = 1; //起點設為1
queue<node>q;
q.push({1, 1}); //把起點放進去
while(!q.empty()) {
int x = q.front().x; //取出隊頭元素
int y = q.front().y;
q.pop();
for (int i = 0; i < 4; i++) {
int tx = x + dx[i], ty = y + dy[i]; //新的一個座標
if(tx < 1 || tx > n || ty < 1 || ty > m || vis[tx][ty] || abs(map1[x][y] - map1[tx][ty]) > d) continue; //判斷是否可以到達新的點
q.push({tx, ty}); //放入佇列
vis[tx][ty] = 1; //進行記錄
}
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if(map2[i][j] && !vis[i][j]) //如果這個點是打卡點並且還沒有走到
return 0; //則返回0
return 1; //否則返回1
}
signed main() {
cin >> n >> m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> map1[i][j];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> map2[i][j];
while(l <= r) {
int mid = (l + r) / 2;
if(check(mid)) ans = min(mid, ans), r = mid - 1; //如果可以,記錄答案,並且看看能否找到更小的
else l = mid + 1; //否則找更大的
}
cout << ans << endl;
return 0;
}