C - Canyon Crossing ( 多隊列BFS )
題目鏈接:https://vjudge.net/problem/%E8%AE%A1%E8%92%9C%E5%AE%A2-4346
5 3 1
1 1 3
3 3 3
0 0 0
2 2 1
1 2 1
樣例輸出
2
題意:n*m的池塘,k個橋,橋的作用是跳過路徑上的一個格子,一個人在第n層(最底層),想要走到最上面,求路徑中的最小值的最大值是多少。
思路:用二分來枚舉路徑上的最小值的最大值,bfs檢驗。bfs要做的事就是,當前只使用大於等於mid的塊和最多k個小於mid的塊,能否從第n層到第1層。
多起點bfs不好維護, 挨個bfs又會超時。今天剛學會了個多隊列bfs
queue<node> Q[1015]; Q[ i ] 的意思是到當前點需要用 i 個橋的點。
之後我們先管Q[ 0 ] 看看只用Q[ 0 ] 能否到達, 在過程中需要用橋的存到Q[ 1 ] 中去。 跑完Q[ 0 ] 再去跑Q[ 1 ] 再跑Q[ 2 ] ...
這樣我們貪心地想,到達確定的點肯定用的橋越少越好,我們的Q正好是從0開始的,所以只需要維護一個via就可以了,複雜度O(n*m)。
代碼:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,k;
int a[1015][1015];
int via[1015][1015];
int nxt[4][2]={0,1,0,-1,1,0,-1,0};
struct node {
int x,y;
}t,d;
int check( int mid )
{
queue<node> Q[1015];
memset(via,0,sizeof(via));
for ( int i=1; i<=m; i++ ) {
via[n][i] = 1;
t.x=n; t.y=i;
if ( a[n][i]<mid ) Q[1].push(t);
else Q[0].push(t);
}
for ( int i=0; i<=k; i++ ) {
while ( !Q[i].empty() ) {
t = Q[i].front(); Q[i].pop();
if ( t.x==1 ) return 1;
for ( int j=0; j<4; j++ ) {
d.x = t.x+nxt[j][0];
d.y = t.y+nxt[j][1];
if ( d.x<=0 || d.y<=0 || d.x>n || d.y>m ) continue;
int op = i;
if ( a[d.x][d.y]<mid ) op++;
if ( via[d.x][d.y]==0 ) {
Q[op].push(d);
via[d.x][d.y] = 1;
}
}
}
}
return 0;
}
signed main()
{
cin >> n >> m >> k;
for ( int i=1; i<=n; i++ ) {
for ( int j=1; j<=m; j++ ) {
scanf("%lld",&a[i][j]);
}
}
int left=0, right=1e9+1,ans=0;
while ( left<=right ) {
int mid = (left+right)/2;
if ( check(mid)==1 ) {
ans = mid;
left = mid + 1;
}
else right = mid - 1;
}
cout << ans << endl;
return 0;
}