题目链接
1.题目大意:给出一个r行c列的棋盘,现在需要从第r行走到第1行,现在能再走的路线上最多建k座桥(建一座桥相当于覆盖掉路线上的一个点),问满足条件的路径最小的最大值
2.首先只看“最小的最大值”这一明显的条件,便知道应该二分答案。不难看出,答案的上界是整个棋盘元素的最大值,下界为0。那么下一步,很明显应该是搜索,如果暴力的话,对第r行所有的点都按照前面二分的答案,求k+1次bfs,显然时间复杂度太高,怎么优化呢?对于当前走过的点,向后走的路径具有一个递推的关系,因为如果能在当前答案下建t座桥,那么就继续走下去,直到需要继续建桥,那么就将路线设置为建t+1座桥的路线。简单来说,就是设置k+1个队列,分别存代表建[0,k]座桥时需要走过的节点,如果某节点大于等于当前答案,那么无论在哪个队列都没有影响,否则就将节点push到下一个队列,即需要多建一座桥才能走的队列
#include <iostream>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn=1005;
typedef pair<int,int> P;
int G[maxn][maxn];
bool vis[maxn][maxn];
int dx[]={1,-1,0,0},dy[]={0,0,1,-1};
int r,c,k;
bool bfs(int cur){
memset(vis,0,sizeof vis);
queue<P> q[k+10];
for(int i=1;i<=c;i++){
vis[r][i]=1;
q[G[r][i]<cur?1:0].push(make_pair(r,i)); //第一次将所有起点判断是不需建桥就能走还是建1座桥能走,入队
}
for(int i=0;i<=k;i++){
while(!q[i].empty()){
P u=q[i].front(); q[i].pop();
if(u.first==1) return 1;
for(int j=0;j<4;j++){
int x=u.first+dx[j];
int y=u.second+dy[j];
if(x<1 || x>=r || y<1 || y>c) continue; //因为已经判断第r行的所有起点,因此这里是大于等于r,条件不能写错否则WA
if(!vis[x][y]){
vis[x][y]=1;
q[G[x][y]<cur?i+1:i].push(make_pair(x,y)); //原理同起点
}
}
}
}
return 0;
}
int main()
{
scanf("%d%d%d",&r,&c,&k);
int L=0,R=0;
for(int i=1;i<=r;i++)
for(int j=1;j<=c;j++){
scanf("%d",&G[i][j]);
R=max(R,G[i][j]);
}
int ans=0;
for(int i=1;i<=100;i++){
int mid=(L+R)>>1;
if(bfs(mid)){
ans=mid;
L=mid+1;
}else R=mid-1;
}
printf("%d\n",ans);
return 0;
}