其实我写过一道dijkstra+二分的题,和这道题类似,但是这道题多一个桥的问题,所以搞得我不知道怎么弄;
题意:给一个n*m的格子,问从最底部的一行的点为起点到第一层的所有路径中的最小值的最大值是多少;
前提是你有k个桥可以用,如果一个格子用了一个桥,那么这个格子的高度就不算入路径中;
分析:
(1)首先来理解这个最小最大值是什么意思:
比如我从Map[n-1][0]这个点出发我能到第一层上去,那么是不是有很多种路径;
我把每一条路径令为:a1,a2,a3,a4…au;
那么起点一共有m个起点,所以我把所有能从最底部到第一层的路径假设为未知数:
a1,a2…,av,表示一共有v条可行的路径;
那么每条路径是不是有一个最小值?
我把每条路径中的最小值给用一个关系表示出来(也就是函数):f(a1),f(a2),…,f(av);
那么现在知道了f(av)表示的就是av这条可行路径中的最小值;
所以我们的答案就是求max(f(a1),f(a2),…,f(av))的值;
所以只要小于最大值(指的是上面的max。ans(这里的ans其实已经是一条路径的最小值了))的格子那么就不能走(如果有桥,那么就放桥走);
如果在mid的情况下,能够到达终点,那么我们就ans变大,使得ans尽可能的最大化;如果不能到达终点,那么说明比mid小的值太多了,舍去的值太多了,所以需要把ans减小,以至于有更多的格子能够走;
(2)
在BFS的时候注意一点:就是必须要枚举完所有的桥之后才能下定论是否能走到终点,其实代码中的i+1和i的选择是具有累计性的,它是累计的前面的用桥的个数;也就是那些值比最小值还要小,那么i就表示那些需要舍去格子所用的桥的数目;
AC代码:
#include<bits/stdc++.h>
using namespace std;
struct Node{
int x,y;
Node(int xx,int yy){
x=xx;y=yy;
}
Node(){
}
};
int n,m,k;
int Map[1010][1010];
int book[1010][1010];
int dir[][2]={
{
-1,0},{
0,1},{
1,0},{
0,-1}};
bool OK(int x,int y){
return x>=0&&x<n&&y>=0&&y<m;
}
bool BFS(int ans){
queue<Node> q[k+10];
for(int i=0;i<n;i++)for(int j=0;j<m;j++) book[i][j]=0;
for(int i=0;i<m;i++){
book[n-1][i]=1;q[Map[n-1][i]<ans?1:0].push(Node(n-1,i));//如果小于答案了,那么就需要用一个桥搞定这个格子
}
for(int i=0;i<=k;i++){
while(q[i].size()){
Node t=q[i].front();
q[i].pop();
if(t.x==0)return true;//如果能在使用最多k个桥能走到第一层,那么说明这个ans合理,所以返回true
for(int j=0;j<4;j++){
int nx=t.x+dir[j][0];
int ny=t.y+dir[j][1];
if(OK(nx,ny)&&!book[nx][ny]){
book[nx][ny]=1;
q[Map[nx][ny]<ans?i+1:i].push(Node(nx,ny));//如果这个值小于答案了,那么就需要一个桥来搞定这个格子,那么桥的数量就会+1
}
}
}
}
return false;//如果不能走到第一层
}
int main(){
scanf("%d %d %d",&n,&m,&k);
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
scanf("%d",&Map[i][j]);
}
}
int L=0,R=1e9+10;//因为这道题的范围是1e9所以我开大一点;
while(L<=R){
int mid=(L+R)>>1;
if(BFS(mid))L=mid+1;
else R=mid-1;
}
cout<<R<<endl;
return 0;
}