4/6 深搜、广搜专题+二分答案+单调队列

本文概述了在经典迷宫问题和动态路障场景下,广度优先搜索(BFS)和深度优先搜索(DFS)的灵活运用。涉及了迷宫路径计数、动态障碍生成的解决方案,以及如何利用广搜求最短路径、深度搜回溯路径。展示了代码实例和关键算法思想。
摘要由CSDN通过智能技术生成

经典迷宫问题:
P6207 [USACO06OCT] Cows on Skates G
1.广搜可用于查找最少需要的步数,而深搜可求出到达终点的路经数。
2.可开一个数组记录路径,最后用深搜回溯路径。注意起点和终点的设置,可能需要自行补上。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int r,c,way[155][155][2];
int dx[4]={-1,1,0,0};
int dy[4]={0,0,1,-1};
char mp[155][155];
bool vis[155][155];
struct node
{
    int x,y,s;
};
queue<node>q;
void dfs(int xx,int yy)
{
    if(!way[xx][yy][0]&&!way[xx][yy][1]) return;
    dfs(way[xx][yy][0],way[xx][yy][1]);
    cout<<way[xx][yy][0]<<" "<<way[xx][yy][1]<<endl;
}
signed main()
{
    cin>>r>>c;
    for(int i=1;i<=r;i++)
        for(int j=1;j<=c;j++)
        cin>>mp[i][j];
    vis[1][1]=1;
    q.push(node{1,1,0});
    while(!q.empty())
    {
        node cur=q.front();q.pop();
        int x=cur.x,y=cur.y;
        if(x==r&&y==c)
            break;
        for(int i=0;i<4;i++)
        {
            int nx=x+dx[i],ny=y+dy[i];
            if(nx<=0||nx>r||ny<=0||ny>c) continue;
            if(!vis[nx][ny]&&mp[nx][ny]=='.')
            {
                vis[nx][ny]=1;
                way[nx][ny][0]=x;way[nx][ny][1]=y;
                q.push(node{nx,ny,cur.s+1});
            }
        }
    }
    dfs(r,c);
    cout<<r<<" "<<c<<endl;
    return 0;
}

2.动态迷宫问题
P3395 路障
在每秒结束后放上一个路障。
关键关键:::注意yes和no输出的格式,看错了即使全敲对了也零分。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2005;
int n,zx[N],zy[N];
int dx[4]={-1,1,0,0};
int dy[4]={0,0,1,-1};
int mp[N][N];
bool vis[N][N];
struct node
{
    int x,y,s;
};
queue<node>q;

signed main()
{
    int t;cin>>t;
    while(t--)
    {
        while(!q.empty()) q.pop();
        memset(mp,0,sizeof(mp));
        memset(vis,0,sizeof(vis));
        int n;cin>>n;
        for(int i=1;i<=2*n-2;i++)
            cin>>zx[i]>>zy[i];
        vis[1][1]=1;
        q.push(node{1,1,0});
        int flag=0;
        while(!q.empty())
        {
            node cur=q.front();q.pop();
            int x=cur.x,y=cur.y,g=cur.s;
            if(x==n&&y==n)
            {
                flag=1;break;
            }
            mp[zx[g]][zy[g]]=1;
            for(int i=0;i<4;i++)
            {
                int nx=x+dx[i],ny=y+dy[i];
                if(nx<=0||nx>n||ny<=0||ny>n)
                    continue;
                if(!vis[nx][ny]&&!mp[nx][ny])
                {
                    vis[nx][ny]=1;q.push(node{nx,ny,g+1});
                }
            }
        }
        if(flag)
            cout<<"Yes"<<endl;
        else
            cout<<"No"<<endl;
    }
    return 0;
}

广搜的灵活应用:
P3906 Geodetic集合
1.先利用广搜对于每个点进行松弛操作,记录到达这个点最短路径的前驱点。
2.根据终点,和记录的前驱点,遍历回起点,记录遍历过的点,方便输出。

#include<bits/stdc++.h>
#define long long ll
using namespace std;
const int inf=0x3f3f3f3f;
const int N=105;
int n,m,k,g[N][N],dist[N],pre[N][N],num[N];
bool vis[N];
void bfs(int u,int v)
{
    memset(num,0,sizeof(num));
    memset(dist,inf,sizeof(dist));
    memset(vis,0,sizeof(vis));
    queue<int>q;dist[u]=0;
    q.push(u);
    while(!q.empty())
    {
        int s=q.front();q.pop();
        if(vis[s]) continue;
        vis[s]=1;
        for(int i=1;i<=n;i++)
        {
            if(g[s][i])
            {
                if(dist[i]>dist[s]+1)
                {
                    dist[i]=dist[s]+1;
                    pre[i][++num[i]]=s;
                    q.push(i);
                }
                else if(dist[i]==dist[s]+1)
                    pre[i][++num[i]]=s;
            }
        }
    }
    memset(vis,0,sizeof(vis));
    q.push(v);vis[v]=1;
    while(!q.empty())
    {
        int s=q.front();q.pop();
        for(int i=num[s];i>=1;i--)
        {
            if(!vis[pre[s][i]])
            {
                vis[pre[s][i]]=1;
                q.push(pre[s][i]);
            }
        }
    }
    for(int i=1;i<=n;i++)
        if(vis[i])
        cout<<i<<" ";
    cout<<endl;
}
signed main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        int u,v;cin>>u>>v;
        g[u][v]=g[v][u]=1;
    }
    cin>>k;
    while(k--)
    {
        int u,v;cin>>u>>v;
        bfs(u,v);
    }
    return 0;
}

深搜经典题目:
P2196 [NOIP1996 提高组] 挖地雷

#include<bits/stdc++.h>
#define long long ll
using namespace std;
const int inf=0x3f3f3f3f;
const int N=55;
int n,a[N],g[N][N],path[N],ans[N],mx,gg;
bool vis[N];
bool check(int u)
{
    for(int i=1;i<=n;i++)
    {
        if(g[u][i]&&!vis[i])
            return 1;
    }
    return 0;
}
void dfs(int id,int step,int sum)
{
    if(!check(id))
    {
        if(mx<sum)
        {
            mx=sum;gg=step;
            for(int i=1;i<=step;i++) ans[i]=path[i];
        }
    }
    for(int i=1;i<=n;i++)
    {
        if(g[id][i]&&!vis[i])
        {
            path[step+1]=i;
            vis[i]=1;
            dfs(i,step+1,sum+a[i]);
            vis[i]=0;
        }
    }
}
signed main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    for(int i=1;i<n;i++)
        for(int j=i+1;j<=n;j++)
    {
        int x;cin>>x;
        if(x)
            g[i][j]=1;
    }
    for(int i=1;i<=n;i++)
    {
        path[1]=i;
        vis[i]=1;
        dfs(i,1,a[i]);
        vis[i]=0;
    }
    for(int i=1;i<=gg;i++)
        cout<<ans[i]<<" ";
    cout<<endl;
    cout<<mx<<endl;
    return 0;
}

P2920 [USACO08NOV]Time Management S
二分枚举起点。注意特判-1的情况!!

#include<bits/stdc++.h>
#define long long ll
using namespace std;
const int inf=0x3f3f3f3f;
const int N=1005;
int n,ans,mx=inf;
struct work
{
    int st,ti,ed;
}e[N];
bool cmp(work e1,work e2)
{
    return e1.ed<e2.ed;
}
bool check(int x)
{
    for(int i=1;i<=n;i++)
    {
        x+=e[i].ti;
        if(x>e[i].ed)
            return 0;
    }
    return 1;
}
signed main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>e[i].ti>>e[i].ed;
        e[i].st=e[i].ed-e[i].ti;
        mx=min(mx,e[i].st);
    }
    sort(e+1,e+n+1,cmp);
    int l=0,r=mx;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(check(mid))
            ans=mid,l=mid+1;
        else
            r=mid-1;
    }
    if(ans)
        cout<<ans<<endl;
    else
        cout<<-1<<endl;
    return 0;
}

P1419 寻找段落
二分答案+单调队列
题意:在一个固定的序列中,找到一个长度在s到t的连续序列,使得其平均值最大。
思路:构造一个函数f(x),呈现单调不递增的性质。当x小时为1;x越大,越会为0。在函数中,
我们将a全部减去mid,问题转化为判断是否存在一个长度在s~t范围内的区间它的和为正,如果有说明还有更大的平均值。

用前缀和和单调队列维护。

然后用单调队列求出sum[i]-min(sum[i-t]~sum[i-s]),然后判断是否大于0即可

#include<bits/stdc++.h>

using namespace std;
const int inf=0x3f3f3f3f;
const int N=1e5+5;
int n,s,t,a[N];
double ans,sum[N];
bool check(double x)
{
    for(int i=1;i<=n;i++)
        sum[i]=sum[i-1]+double(a[i])-x;
    int head=1,tail=0,q[N];
    for(int i=s;i<=n;i++)
    {
        while(head<=tail&&sum[q[tail]]>sum[i-s])
            tail--;
        q[++tail]=i-s;
        while(head<=tail&&q[head]<i-t)
            head++;
        if(head<=tail&&sum[i]-sum[q[head]]>=0)
            return 1;
    }
    return 0;
}
signed main()
{
    cin>>n>>s>>t;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    double l=-10000,r=10000;
    while(r-l>1e-5)
    {
        double mid=(l+r)/2;
        if(check(mid))
            ans=mid,l=mid;
        else
            r=mid;
    }
    printf("%.3lf\n", ans);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值