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;
}