BFS
STL
queue<pair<int,int> > q;//定义
q.push(make_pair(x,y));//入队
//取队首
xx=q.front().first;//第一个值
yy=q.front().second;//第二个值
q.pop();//出队
/************************************************************
广度优先搜索算法的基本思想:
1、对于初始状态入队,设置初始状态为已访问
2、如果队列不为空时,出队队头元素,否则跳到第5步
3、检查出队的元素是否为最终解,如果是则跳到第5步。
4、对于出队的元素,检查所有相邻状态,如果有效并且未访问,则将
所有有效的相邻状态进行入队,并且设置这些状态为已访问,然后
跳到第2步重复执行
5、检查最后出队的元素是否为最终解,如果是输出结果,否则说明无解
广度优先搜索是借助于队列这种数据结构进行搜索的,队列的特点是先
进先出(FIFO),通过包含queue这个队列模板头文件,就可以利用c++
的队列模板定义自己的队列了,队列的操作非常简单,主要有以下几个:
q.push() 入队操作
q.front() 取队头元素
q.pop() 队头元素出队
q.size() 获取队列的元素个数
q.empty() 判断队列是否为空,为空返回true,不为空返回false
广度优先搜索算法的关键是要搞清楚求解过程中每一步的相邻状态有哪些,
每个状态需要记录什么信息,在搜索过程中如何标记这些状态为已访问。
在本题中,相邻状态为当前所在楼层通过按向上或向下按钮所能到达的楼
层,每个状态要记录的信息包括楼层编号和按按钮的次数。
*************************************************************/
P1443 马的遍历
最少要走几步(不能到达则输出 −1)
#include <bits/stdc++.h>
using namespace std;
int n, m, x, y, ans[405][405], vis[405][405];
int dx[8] = {-1, -2, -2, -1, 1, 2, 2, 1};//棋盘上马走“日”字,因此方向有所不同
int dy[8] = {2, 1, -1, -2, 2, 1, -1, -2};
queue<pair<int, int>>q;
int main()
{
cin >> n >> m >> x >> y;
memset(ans, -1, sizeof(ans));
ans[x][y] = 0; //自己到自己:0 步
vis[x][y] = 1;
q.push(make_pair(x, y)); //进队
while(!q.empty()){ //当队不空时,判断
int xx = q.front().first;
int yy = q.front().second;
q.pop(); //取队首并出队
for(int i=0; i<8; i++){ //8个方向 bfs
int tx = xx + dx[i];
int ty = yy + dy[i];
if(tx<1 || tx>n || ty<1 || ty>m || vis[tx][ty]) continue;
vis[tx][ty] = 1;
q.push(make_pair(tx, ty));//进队
ans[tx][ty] = ans[xx][yy] + 1;//前一格步数 + 1
}
}
for(int i=1; i<=n; i++){
for(int j=1; j<=m; j++){
cout << ans[i][j] << " ";//!复制样例输出空格!
}
cout << endl;
}
return 0;
}
P1135 奇怪的电梯
至少要按几次按钮呢
dfs 第一个数据点超时
#include <bits/stdc++.h>
using namespace std;
int n, a, b, k[205], vis[205], d[2]={1, -1};//上下
int ans = 1e6;
void dfs(int a, int step){
if(a == b) ans = min(ans, step);
if(step > ans) return ;
vis[a] = 1;
for(int i=0; i<2; i++){
int ty = a + k[a] * d[i];
if(ty<1 || ty>n || vis[ty])continue;
dfs(ty, step+1);
vis[ty] = 0;
}
}
int main()
{
cin >> n >> a >> b;
for(int i=1; i<=n; i++) cin >> k[i];
dfs(a, 0);
if(ans == 1e6) cout << -1 << endl;
else cout << ans << endl;
return 0;
}
bfs
#include <bits/stdc++.h>
using namespace std;
int n, a, b, k[205], vis[205], d[2] = {1, -1};//上下
queue<pair<int, int>> q;
int id, step, td;
int main()
{
cin >> n >> a >> b;
for(int i=1; i<=n; i++) cin >> k[i];
q.push(make_pair(a, 0));
while(!q.empty()){
id = q.front().first;
step = q.front().second;
q.pop();
if(id == b) break;
for(int i=0; i<2; i++){
td = id + k[id] * d[i];
if(td>=1 && td<=n && !vis[td]){
q.push(make_pair(td, step + 1));
vis[td] = 1;
}
}
}
if(id != b) cout << -1 << endl;
else cout << step << endl;
return 0;
}
P2895 [USACO08FEB]Meteor Shower S
最少需要多少时间才能到达一个安全的格子。如果不可能到达输出 −1。
无最大边界
流星的力量会将它所在的格子,以及周围 4 个相邻的格子都化为焦土
#include <bits/stdc++.h>
using namespace std;
int m, x, y, t, tx, ty, time1[305][305], vis[305][305];//(x,y)最早流星到达时间
int dx[4]={0, 0, 1, -1}, dy[4]={1, -1, 0, 0};
struct node{
int x, y, t;//当前时间
}n;
queue<node> q;
int main()
{
cin>>m;
memset(time1, -1, sizeof(time1));
for(int i=0; i<m; i++)
{
cin >> x >> y >> t;
if(t<time1[x][y] || time1[x][y]==-1) //这颗流星到达的时间必须小于前面流星或焦土到达的时间,或者还暂时没有流星及焦土
time1[x][y] = t;
for(int i=0; i<4; i++){
tx = x + dx[i], ty = y + dy[i];
if(tx>=0 && ty>=0 && (time1[tx][ty]==-1 || t<time1[tx][ty]))
time1[tx][ty]=t; //枚举焦土
}
}
vis[0][0]=1;
q.push({0, 0, 0});
while(!q.empty())
{
n=q.front();
q.pop();
for(int i=0; i<4; i++)
{
tx = n.x + dx[i], ty = n.y + dy[i];
if(tx>=0 && ty>=0 && !vis[tx][ty] && (time1[tx][ty]==-1 || n.t+1 < time1[tx][ty])) //没有流星到过或者bessie到这个格子的时候流星还没有到达
{
vis[tx][ty]=1; //扩展节点
q.push({tx, ty, n.t + 1});
if(time1[tx][ty] == -1){
cout << n.t + 1 << endl; //输出答案
return 0;
}
}
}
}
cout << -1 << endl;
return 0;
}
P1019 [NOIP2000 提高组] 单词接龙
输出以此字母开头的最长的“龙”的长度===>dfs
#include <bits/stdc++.h>
using namespace std;
int n, len, vis[25];
string s[25], tmp;
int canlink(string s1, string s2){
for(int i=1; i<min(s1.length(), s2.length()); i++){//枚举重叠长度
int flag = 1;//有重叠
for(int j=0; j<i; j++)
if(s1[s1.length() - i + j] != s2[j]) flag = 0;//不等说明重叠长度不对
if(flag) return i;//返回重叠长度
}
return 0;//无重叠
}
void dfs(string a, int lnow){
len = max(len, lnow);
for(int i = 0; i < n ;i++){
int t = canlink(a, s[i]);//重叠长度
if(t && vis[i] < 2){
vis[i]++;
dfs(s[i], lnow + s[i].length() - t);
vis[i]--;
}
}
}
int main()
{
cin >> n;
for(int i=0; i<=n; i++) cin >> s[i];
//当s[n]为一个字母时,保证从最后一位比较
dfs(' ' + s[n], 1);
cout << len << endl;
return 0;
}
P1825 [USACO11OPEN]Corn Maze S
起点到出口所需的最短时间===>bfs
重点:装置传送用一个函数表示,可以不止用一次!因此不需要标记是否访问过
迷宫里有一些传送装置,可以将奶牛从一点到另一点进行瞬间转移。这些装置可以双向使用。
如果一头奶牛处在这个装置的起点或者终点,这头奶牛就必须使用这个装置,且不花费时间。
#include <bits/stdc++.h>
using namespace std;
int n, m, vis[305][305], fx, fy, sx, sy, tx, ty;
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0};
char mz[305][305];
struct node{
int x, y, t;
}p;
queue<node> q;
void go(int &x, int &y){//跳到另一个装置,并改变坐标 注意:+ &
for(int i=1; i<=n; i++){
for(int j=1; j<=m; j++){
if(mz[i][j] == mz[x][y] && (i!=x || j!=y)){
x = i; y = j;
return ;
}
}
}
}
int main()
{
cin >> n >> m;
memset(mz, '#', sizeof(mz));//在外圈加一层玉米地
memset(vis, 1, sizeof(vis));//初始化为都不可以访问
for(int i=1; i<=n; i++){
for(int j=1; j<=m; j++){
cin >> mz[i][j];
if(mz[i][j] == '=')fx = i, fy = j;//出口坐标
if(mz[i][j] == '@')sx = i, sy = j;//起点坐标
if(mz[i][j] != '#')vis[i][j] = 0;//除了玉米地外 可以访问
}
}
vis[sx][sy] = 1;
q.push({sx, sy, 0});//起点开始 bfs
while(!q.empty()){
p = q.front();
q.pop();
if(p.x == fx && p.y == fy){//找到终点输出并跳出循环
cout << p.t << endl;
break;
}
if(mz[p.x][p.y] >='A' && mz[p.x][p.y] <='Z')//遇到传送装置
go(p.x, p.y); //直接跳到另一个
for(int i=0; i<4; i++){//四周开始搜索
tx = p.x + dx[i];
ty = p.y + dy[i];
if(!vis[tx][ty]){
vis[tx][ty] = 1;
q.push({tx, ty, p.t + 1});
}
}
}
return 0;
}