计蒜客43467 Canyon Crossing(二分答案+多队列bfs)

题目链接
在这里插入图片描述
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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值