搜索:flood fill,DFS连通性与剪枝优化

BFS flood fill

  flood fill模型可以在线性的时间内,找到一个连通块。例子:城堡问题
  思路:枚举每一个格子,判断它是否于其他块连通,如果连通,扩展其他块。直到不连通为止,更新最大面积。

#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn = 55;
const int dx[4]={0,-1,0,1},dy[4]={-1,0,1,0};
int n,m;
int g[maxn][maxn];
int cnt,ans;
bool st[maxn][maxn];
typedef pair<int,int> PII;
queue<PII> q;
void bfs()
{
    int s = 1; //面积
    while(q.size())
    {
        int x = q.front().first,y = q.front().second;
        q.pop(); //前两行宽搜模板
        for(int i = 0;i < 4;i++)
        {
            int xx = x + dx[i],yy = y + dy[i]; //枚举4个方向
            //本题在枚举方向上的小技巧,利用二进制枚举。从低位到高位对应题干的4个方向。
            if(((g[x][y] >> i) & 1) != 0) continue;  
            if(xx < 0 || xx >= n || yy < 0 || yy >= m || st[xx][yy]) continue; //判断是否出界
            st[xx][yy] = true;
            s++; //更新面积
            q.push({xx,yy}); //入队访问
        }
    }
    cnt++; //房间数目 + 1
    ans = max(ans,s); //更新最大面积
}
int main()
{
    cin>>n>>m;
    for(int i = 0;i < n;i++)
    {
        for(int j = 0;j < m;j++)
        {
            scanf("%d",&g[i][j]);
        }
    }
    for(int i = 0;i < n;i++)
    {
        for(int j = 0;j < m;j++)
        {
            if(!st[i][j]) //	当前格子,没有被访问
            {
                st[i][j] = true; //标记访问 防止重复搜索
                q.push({i,j});//入队
                bfs();
            }
        }
    }
    cout<<cnt<<endl<<ans<<endl;
    return 0;
}

多源BFS
  多源BFS指的是,利用BFS求最短路问题时,起点有多个。需要我们做出优化。多源BFS
  思路:对于该题而言,如果枚举每一个0,找到该0到它最近的1之间的距离,算法超时。优化的方案是,首先,将所有的1入队,扩展所有距离为1的点。将所有距离为1的点入队,在扩展出到1的距离为2的点,直至所有点被遍历。因为,每个点都只被遍历了1次,因为复杂度为O(n).

#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn = 1010;
const int dx[4] = {-1,0,1,0},dy[4] = {0,1,0,-1};
int n,m;
char g[maxn][maxn];
int dis[maxn][maxn];
bool st[maxn][maxn];
typedef pair<int,int> PII;
queue<PII> q;
void bfs()
{
    while(q.size())
    {
        int x = q.front().first, y = q.front().second;
        q.pop();
        for(int i = 0;i < 4;i++)
        {
            int xx = x + dx[i], yy = y + dy[i]; //枚举到当前点距离为1的点
            if(xx < 0 || xx >= n || yy < 0 || yy >=m) continue;
            if(!st[xx][yy])
            {
                dis[xx][yy] = dis[x][y] + 1; //更新距离
                st[xx][yy] = true; //标记访问
                q.push({xx,yy}); //入队,进行下次扩展
            }
        }
    }
}
int main()
{
    cin>>n>>m;
    for(int i = 0;i < n;i++)
    {
        scanf("%s",g[i]);
    }
    for(int i = 0;i < n;i++)
    {
        for(int j = 0;j < m;j++)
        {
            if(g[i][j] == '1') //初始状态入队
            {
                q.push({i,j});
                st[i][j] = true; //标记访问,防止重复。            
            }
        }
    }
    bfs();
    for(int i = 0;i < n;i++)
    {
        for(int j = 0;j < m;j++)
        {
            printf("%d ",dis[i][j]);
        }
        cout<<endl;
    }
    return 0;
}

DFS连通性

  对于DFS问题而言,它的核心在与找到一种合适的搜索顺序,使得按照该搜索顺序可以搜索到所有的方案,从而正确求解。
  例子:迷宫。对于本题而言,它的搜索顺序就是图中的所有方向,具体而言,对于每个点枚举它所有可能的方向,如果起点对于终点是可达的,那么一定可以搜索到最优解。

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 110;
const int dx[5] = {0,-1,0,1,0},dy[5] ={0,0,1,0,-1};
char g[maxn][maxn];
bool st[maxn][maxn];
int k,n;
int ha,hb,la,lb;
bool flag;
void dfs(int st_x,int st_y)
{
    if(st_x == la && st_y == lb) //表示已经搜索到终点返回
    {  
        st[la][lb] = true;
        return ;
    }
    st[st_x][st_y] = true; //防止重复访问
    for(int i=1;i <= 4;i++)
    {
        int xx = st_x + dx[i],yy = st_y + dy[i]; //枚举所有方向
        if(xx < 0 || xx >= n || yy <0 ||yy >= n) continue; //判断界内
        if(!st[xx][yy] && g[xx][yy] != '#' && st[la][lb] == false) 
        {
            dfs(xx,yy); //第26行
        }
    }
    return ;
}
int main()
{
    cin>>k;
    while(k--)
    {
        scanf("%d",&n);
        for(int i = 0;i < n;i++)
        {
            scanf("%s",g[i]);
        }
        scanf("%d%d%d%d",&ha,&hb,&la,&lb);
        st[ha][hb] = true;
        if(g[ha][hb] == '#' || g[la][lb] == '#') //题干的边界条件
        {
            puts("NO");
            st[ha][hb] = false;
            continue;
        }
        else
        {
            dfs(ha,hb);
        }
        if(st[la][lb]) puts("YES");
        else puts("NO");
        memset(st,false,sizeof st);
    }
}

对于该题而言,不需要回溯,如果回溯的话反而会超时。原因如下:
  1.改题只需要判断出连通性。假设起点记为x,y。此时的dfs记为dfs1。对于x,y而言,当执行到第26行代码时,如果df1函数能够继续执行,那么一定已经完成了一条路径的搜索。如果该路径中包含了终点,证明可达,不需要继续进行搜索。而如果不可达,则证明经过这条路径上任意一个点都不可达,其他路径不应该经过这个点,因此不需要回溯。
DFS剪枝优化
常见的剪枝策:
  (1)优化搜索顺序,大部分情况下,我们应该选择分支数量较少的节点。这样可以加快搜素顺序
  (2)排除等效冗余。例如,对于求组合问题而言,1,2和2,1是等效的状态,可以排除。
  (3)可行性剪枝。方案不可行退出。
  (4)最优性剪枝。当前方案,不会成为最优值。
例子:小猫爬山
  1.搜索思路,对于第u只小猫而言,枚举所有的车,如果它可以被放在某车内,将其放入,也可以为它新租一辆车。搜索所有的方案,可以得出最优解。

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 20;
int n,m;
int w[maxn];
int sum[maxn];
int ans = maxn;
bool cmp(int a,int b)
{
    return a>b;
}
void dfs(int u,int k) //u表示第u只小猫,k表示第k辆车
{
    if(k >= ans) return ; //最优性剪枝,当k>=ans时,当前方案不会成为最优解。
    if(u == n) ans = k;
    for(int i = 0;i < k;i++)
    {
        if(sum[i] + w[u] <= m) //可行性剪枝
        {
            sum[i] += w[u];
            dfs(u + 1,k); //将第u只小猫放到第i量车中
            sum[i] -= w[u];
        }
    }
    sum[k] = w[u];  //将第u只小猫放到第k+1量车中
    dfs(u + 1,k + 1);
    sum[k] = 0; 
}
int main()
{
    cin>>n>>m;
    for(int i = 0;i < n;i++) cin >> w[i];
    sort(w,w+n,cmp); //优化搜索顺序,因为先排序较重的小猫,可以带来使得分枝数目更少。
    dfs(0,0);
    cout<<ans;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值