一、宽搜的一些注意点
(摘自挑战程序设计竞赛)
1.搜索顺序:从初始状态由近及远,即开始状态 -> 转移一次就可以到达的所有状态 -> 转移两次可以到达的所有状态 ->...
2.很容易用于求解最短路径、最小操作次数等等。
3.将已经访问过的状态标记起来。
4.搜索时先将初始状态加入队列,从队列最前端不断取出状态,把该状态可以转移到的状态中还没有被访问的部分加入队列,如此往复,直到队列取空或者找到答案。
//这样保证了是由近及远的顺序
二、CF920A Water The Garden
CF920A Water The Garden - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题意:
有一个长度为 n 的花园,一共有 k 个水龙头,分别在x_1,x_2,…xk 的位置。第 t秒每个水龙头可以浇灌到它的前第 t-1 个方格和它后第 t-1 个方格,第 1 秒时则只浇灌自己所在的方格,问浇灌完花园的时间。
感谢@伏轩彤666 提供的翻译
1.初步想法
(好像有人说这不是bfs),就是模拟,主要是因为时间每次加一,就像宽搜是按照从开始状态由近及远的顺序去搜索一样,然后用vis标记每次浇到的方格(避免tot重复统计),用tot统计总数(当浇够了就输出t),所以bfs每次传的参数也是t。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int water[maxn],n,k;
int vis[maxn],tot;
void bfs(int t)
{
for(int i=0;i<k;i++)//枚举水龙头
{
if(water[i]-t+1>0&&water[i]-t+1<=n&&!vis[water[i]-t+1])
vis[water[i]-t+1]=1,tot++;
if(water[i]+t-1<=n&&water[i]+t-1>0&&!vis[water[i]+t-1])
vis[water[i]+t-1]=1,tot++;
}
if(tot==n)
{
cout<<t<<endl;
return;
}
else
bfs(t+1);
}
int main()
{
int T;
cin>>T;
int ans=0;
while(T--)
{
cin>>n>>k;
memset(vis,0,sizeof(vis));
tot=0;
for(int i=0;i<k;i++)
{
cin>>water[i];
}
bfs(1);
}
//cout<<ans;
}
2.向外拓展的队列bfs
每一个水龙头都可以向外拓展,拓展1秒就相当在新位置上新装了个水龙头,每个水龙头向外喷水的距离都是1,然后每次把新水龙头push进队列且把时间++,把旧水龙头pop出去就成,用ans维护最大的时间。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int water[maxn],n,k;
int vis[maxn],tot;
struct node
{
int s,time;
};
queue<node> q;
int ans=0;
int main()
{
int T;
cin>>T;
int ans=0;
while(T--)
{
cin>>n>>k;
memset(vis,0,sizeof(vis));
for(int i=0;i<k;i++)
{
cin>>water[i];
node temp;//临时设置一个点,用来暂时存水龙头,再放到队列里
temp.s=water[i];
temp.time=1;
q.push(temp);
vis[water[i]]=1;//第一秒所有水龙头脚下的地方被标记
}
while(!q.empty())
{
node x=q.front();
q.pop();//把队列头拿出来看看
ans=max(ans,x.time);//时间是取每个点用时的最大值
//如果所有都vis了,就不会进入下面两个if
if(x.s-1>0&&x.s-1<=n&&!vis[x.s-1])//向左拓展,时间++,作为下一个考察点,放入队列
{
node temp;
temp.s=x.s-1;
temp.time=x.time+1;
q.push(temp);
vis[x.s-1]=1;
}
if(x.s+1>0&&x.s+1<=n&&!vis[x.s+1])//向右拓展,时间++
{
node temp;
temp.s=x.s+1;
temp.time=x.time+1;
q.push(temp);
vis[x.s+1]=1;
}
}
cout<<ans<<endl;
ans=0;
//cout<<ans;
}
}
三、01迷宫
传送门:P1141 01迷宫 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
是这个样子的,处于一个连通块内的方格所可以走到的点的个数是相同的,算出来一个就把这个连通块内所有点都记上答案number(所以还要记录连通块走过的点past)。
另外就是放入队列的首先是查询的起始点,每次取出队首后,要把可以走到的点push进去(不超过边界且和取出来的点字符不一样)。
当然看过的点,我们就用book标记一下,就不再看了,反正都 已经是一个连通块里的人 或者是 别家连通块里的人,就不再看了。
本来想学一下用pair存点的,可是这个dev真的很失落。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+5;
const int maxm=1e3+5;
const int INF=1e8;
typedef pair<int,int> P;
int N,M;
char maze[maxn][maxm];//存迷宫
int sx,sy,gx,gy;//起始点和终点的坐标
int book[1001][1001];//保存各个点所在的连通图,以及是非被处理过
int step[maxn][maxn];//保存每个连通块的大小
int vis[maxn];
int past[1000001][3];
//向四个方向移动的向量
int dx[4]={1,0,-1,0};
int dy[4]={0,1,0,-1};
int bfs(int x,int y)
{
int number=1;//可以到达的地点的个数
past[number][1]=x;//记录走过的点
past[number][2]=y;
int nowx,nowy;
queue<int> quex;
queue<int> quey;
book[x][y]=1;//标记起点,放入队列
quex.push(x);
quey.push(y);
while(quex.size()!=0)
{
quex.pop();
quey.pop();
for(int i=0;i<4;i++)
{
nowx=x+dx[i];
nowy=y+dy[i];
if(book[nowx][nowy]==0&&maze[x][y]!=maze[nowx][nowy]&&nowx>0&&nowx<=N&&nowy>0&&nowy<=N)
{
number++;
past[number][1]=nowx;//记录走过的点
past[number][2]=nowy;
book[nowx][nowy]=1;
quex.push(nowx);
quey.push(nowy);
}
}
x=quex.front();
y=quey.front();
}
for(int i=1;i<=number;i++)//再将每一个成员能到的点的个数分别记录下来!
//下次见到这些点直接输出答案就行了,因为在连通块里,到的个数都一样哦~~
step[past[i][1]][past[i][2]]=number;
return number;
}
int main()
{
char c;
cin>>N>>M;
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
{
cin>>maze[i][j];//存入地图
}
for(int t=1;t<=M;t++)
{
int x,y;
cin>>x>>y;
if(book[x][y]==0)
{
bfs(x,y);
cout<<step[x][y]<<endl;
}
else
cout<<step[x][y]<<endl;
}
}