BFS其实含“最短路”的一个含义
最小步数、最短距离、最少操作次数——BFS
DFS模版
int search(int t)
{
if(满足输出条件)
{
输出解;
}
else
{
for(int i=1;i<=尝试方法数;i++)
if(满足进一步搜索条件)
{
为进一步搜索所需要的状态打上标记;
search(t+1);
恢复到打标记前的状态;//也就是说的{回溯一步}
}
}
}
全排列的DFS题目
DFS最难的是代码的实现,思路应该都没有什么问题
这个题中,使用st数组来判断每一位上的数字有没有被重复使用,然后dfs
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int n;
const int N = 100;
int path[N];
int st[N];
void dfs(int u)//u是第几位
{
if(u==n+1)
{
for(int i=1;i<=n;i++)
{
cout<<path[i]<<" ";
}
cout<<endl;
return;
}
for(int i=1;i<=n;i++)//这里的i代表的是全排列中被使用的数字
{
if(st[i]==0)//没被用过的数字i
{
path[u]=i;
st[i]=1;//记录的是有没有重复数字
dfs(u+1);
st[i]=0;
}
}
}
int main()
{
cin>>n;
dfs(1);
return 0;
}
全排列的拓展题目
#include <bits/stdc++.h>
using namespace std;
const int N = 10;
int target;
int num[N];
int calc(int l, int r) {
int res = 0;
for (int i = l; i <= r; i++) {
res = res * 10 + num[i];
}
return res;
}
int main() {
cin >> target;
for (int i = 0; i < 9; i++) {
num[i] = i+1 ;
}
int res = 0;
do {
for (int i = 0; i < 9; i++) {
for (int j = i + 1; j < 9; j++) {
int a = calc(0, i);
int b = calc(i + 1, j);
int c = calc(j + 1, 8);
if (a == 0 || b == 0 || c == 0) {
continue;
}
if (a * c + b == c * target) {
++res;
}
}
}
// 调用函数生成全排列
} while (next_permutation(num, num + 9));
cout << res << '\n';
return 0;
}
STL里面的库函数实现
#include<bits/stdc++.h>
using namespace std;
int a[8];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)a[i]=i,printf("%d ",i);
while(next_permutation(a+1,a+1+n)){
printf("\n");
for(int i=1;i<=n;i++)printf("%d ",a[i]);
}
}
找到一个很类似的DFS
考前临时抱佛脚
每一组的左半边和右半边的暴力搜索
#define _CRT_SECURE_NO_WARNINGS 1
#include<bits/stdc++.h>
using namespace std;
const int N = 30;
int s[N];
int a[N][N];//a[i][j]表示
int Left,Right,ans,minn;
void dfs(int zu, int shu)//第zu组的第shu个数
{
if (shu > s[zu])//叶子结点是:这一组已经搜索完成
{
minn = min(minn, max(Left, Right));
return ;
}
Left += a[zu][shu];//处理左半边
dfs(zu, shu + 1);
Left -= a[zu][shu];//还原
Right += a[zu][shu];
dfs(zu, shu + 1);
Right -= a[zu][shu];
}
int main()
{
for (int i = 1; i <= 4; i++)cin >> s[i];
for (int i = 1; i <= 4; i++)
{
Left = Right = 0;
minn = 0x3f3f3f3f;
for (int j = 1; j <=s[i]; j++)//j代表第i组的第j个数
{
cin >> a[i][j];
}
dfs(i, 1);//这个dfs表示从第i组的第1个数开始选择
ans += minn;
}
cout << ans;
return 0;
}
基础的DFS
学习STL语言可以省去很多的代码
这个题的dfs的参数dfs(i,j,u,num) 分别是当前点的坐标+当前已经有的位数+当前生成的数的值
使用unordered_set<int>S;
C++总结(7):STL无序容器之unordered_set、unordered_map、unordered_multiset、unordered_multimap详解-优快云博客
这个题的代码如下
#include <iostream>
#include <cstring>
#include <algorithm>
#include<unordered_set>
using namespace std;
const int N = 5;
int g[N][N];
int n,m,k;
unordered_set<int>S;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
void dfs(int x,int y,int u,int num)
{
if(u==k)
{
S.insert(num);return;//开了一个集合
}
for(int i=0;i<4;i++)
{
int a=x+dx[i];
int b=y+dy[i];
if(a>=0&&a<n&&b>=0&&b<m)dfs(a,b,u+1,num*10+g[a][b]);
}
}
int main()
{
cin>>n>>m>>k;
//初始化
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
cin>>g[i][j];
}
}
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
dfs(i,j,0,g[i][j]);
}
}
cout<<S.size()<<endl;
return 0;
}
比较类似的一道DFS题
#include <algorithm>
#include <iostream>
using namespace std;
const int N = 2e1;
int cat[N], cab[N];
int n, w;
int ans;
bool cmp(int a, int b) {
return a > b;
}
void dfs(int now, int cnt) {
if (cnt >= ans) {
return;
}
if (now == n + 1) {
ans = min(ans, cnt);
return;
}
//尝试分配到已经租用的缆车上
for (int i = 1; i <= cnt; i++) { //分配到已租用缆车
if (cab[i] + cat[now] <= w) {
cab[i] += cat[now];
dfs(now + 1, cnt);
cab[i] -= cat[now]; //还原
}
}
// 新开一辆缆车
cab[cnt + 1] = cat[now];
dfs(now + 1, cnt + 1);
cab[cnt + 1] = 0;
}
int main() {
cin >> n >> w;
for (int i = 1; i <= n; i++) cin >> cat[i];
sort(cat + 1, cat + 1 + n, cmp);
ans = n;
dfs(1, 1);
cout << ans << endl;
return 0;
}
八皇后DFS
[USACO1.5] 八皇后 Checker Challenge - 洛谷
#include <iostream>
using namespace std;
const int N = 20;
char g[N][N];
int n;
bool col[N], dg[2 * N], udg[2 * N]; // 扩大 dg 和 udg 的大小
int cnt = 0;
int ans = 0;
void dfs(int u)
{
if (u == n + 1)
{
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (cnt < 3)
{
if (g[i][j] == 'Q')
printf("%d ", j);
}
}
}
if (cnt < 3) printf("\n");
cnt++;
return;
}
for (int i = 1; i <= n; i++)
{
if (!col[i] && !dg[u + i] && !udg[n - u + i + n]) // 调整索引
{
g[u][i] = 'Q';
col[i] = dg[u + i] = udg[n - u + i + n] = true; // 调整索引
dfs(u + 1);
col[i] = dg[u + i] = udg[n - u + i + n] = false; // 调整索引
g[u][i] = '.';
}
}
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
g[i][j] = '.';
}
}
dfs(1);
cout << cnt << endl;
return 0;
}
和几何知识结合的DFS
这个题,每一个点在扩展时候的最大边长有两个方面的制约,第一个就是x和y方向上的边框制约,第二个就是其他油滴半径的制约(直接计算两个圆心之间的距离,利用高中的圆相切的知识可以得到),此外,我第一次在做这个题的时候,不知道dfs的回溯怎么实现,看了题解之后发现开一个st数组标记就可以实现回溯(相当于直接撤销了一个圆)。
这个题的框架中规中矩
#include<bits/stdc++.h>
using namespace std;
const int N=10;
const double PI=3.1415926535;
bool st[N];
double x[N],y[N],r[N],xa,ya,xb,yb,maxans;
int n;
//计算当前点的半径的函数
double cal_r(int i)
{
double min_xx=min(abs(x[i]-xa),abs(x[i]-xb));//x方向上
double min_yy=min(abs(y[i]-ya), abs(y[i] - yb)); // y向上
double ans=min(min_xx,min_yy);
for(int j=1;j<=n;j++){
if(i!=j&&st[j])
{
double d = sqrt((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]));
ans=min(ans,max(d-r[j],0.0));//这里有个坑点
}
}
return ans;
}
void dfs(int k,double sum)
{
if(k>n){
maxans=max(maxans,sum);
return ;
}
for(int i=1;i<=n;i++)
{
if(st[i]==0)
{
r[i]=cal_r(i);
st[i]=true;//只需要开一个标记数组就可以回溯,原来这么简单
dfs(k+1,sum+PI*r[i]*r[i]);
st[i]=false;
}
}
}
int main()
{
double tot_area;
scanf("%d", &n);
scanf("%lf%lf%lf%lf", &xa, &ya, &xb, &yb);
tot_area = abs(xa - xb) * abs(ya - yb);
for (int i = 1; i <= n; i++)scanf("%lf%lf", &x[i], &y[i]);
dfs(1, 0);
printf("%d", int(tot_area - maxans + 0.5)); // 四舍五入
return 0;
}
状态类的DFS
主要是积累做题经验,将题目中的关键信息提炼出来
3417. 砝码称重 - AcWing题库(有限制的选择问题)
每个砝码有三种情况,左边,右边,不放
我拿到这样的题目,思维总是转不到关键点上,我会从大的方面考虑,而不是从每个砝码的状态进行考虑,导致我经常没有正确的思路
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110,M=200010;
//st数组用来储存遍历过的情况,如果搜过了这个点,就不继续dfs了,节省时间。
//记忆化搜索。
bool st[N][M];
bool f[M];
int n;
int a[N];
void dfs(int u,int sum)
{
if(st[u][sum])return ;
st[u][sum]=true;
f[sum]=true;
if(u>n)return ;
dfs(u+1,sum+a[u]);
dfs(u+1,sum);
dfs(u+1,abs(sum-a[u]));//负数?
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
dfs(1,0);//当前第几个数 称出的重量
int ans=0;
for(int i=1;i<=M;i++)
{
if(f[i])ans++;
}
cout<<ans<<endl;
return 0;
}
邻接表(距离类)+DFS
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int e[21][21];
int n,m;
bool st[N];
int res,ans;
void dfs(int pos,int sum)
{
ans=max(ans,sum);
for(int i=1;i<=n;i++)
{
if(e[pos][i]>0&&st[i]==0)
{
st[i]=1;
dfs(i,sum+e[pos][i]);
st[i]=0;
}
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int x,y,dis;
cin>>x>>y>>dis;
e[x][y]=dis;
e[y][x]=dis;
}
for(int i=1;i<=n;i++)
{
st[i]=1;//已经走过了
dfs(i,0);//当前的点+已经走过的距离
res=max(res,ans);
memset(st,0,sizeof st);
}
cout<<res<<endl;
return 0;
}
经典的DFS(暴力搜索)上下左右(题解里面一般称之为打表)
maze[i][j]=0的时候表示走不通,注意!要把起点设置为0!
#include<bits/stdc++.h>
using namespace std;
const int N = 10;
int maze[N][N];
int n, m, t, sx, sy, fx, fy;
int ans = 0;
int dx[4] = { 0,0,-1,1 }, dy[4] = { -1,1,0,0 };
void dfs(int x, int y)
{
if (x == fx && y == fy)
{
ans++; return;
}
for (int i = 0; i < 4; i++)
{
if (maze[x + dx[i]][y + dy[i]] == 1) {
maze[x + dx[i]][y + dy[i]] = 0;
dfs(x + dx[i], y + dy[i]);
maze[x + dx[i]][y + dy[i]] = 1;
}
}
}
int main()
{
cin >> n >> m >> t;
cin >> sx >> sy >> fx >> fy;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
maze[i][j] = 1;
}
}
for (int i = 0; i < t; i++)
{
int x, y;
cin >> x >> y;
maze[x][y] = 0;
}
maze[sx][sy] = 0;
dfs(sx, sy);
cout << ans;
return 0;
}
自然数拆分
using namespace std;
int a[10001]={1},n;
int search(int,int);
int print(int);
int main()
{
cin>>n;
search(n,1);//将要拆分的数n传递给s
return 0;
}
int search(int s,int t)
{
int i;
for(i=a[t-1];i<=s;i++)
if(i<n)//当前数i要大于等于前一位数,且不超过n
{
a[t]=i;//保存当前拆分的数i
s-=i;//s减去数i,s的值将继续拆分
if(s==0)print(t);//当s=0时,拆分结束输出结果
else search(s,t+1);//当s>0时,继续递归
s+=i;//回溯:加上拆分的数,以便产生所有可能的拆分
}
}
int print(int t)
{
for(int i=1;i<=t-1;i++)//输出一种拆分方案
cout<<a[i]<<"+";
cout<<a[t]<<endl;
}
单词接龙-DFS
(不过更多的还是在考察字符串)
P1019 [NOIP2000 提高组] 单词接龙 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
#define _CRT_SECURE_NO_WARNINGS 1
#include<bits/stdc++.h>
using namespace std;
string ss[25];
int n, mark[25], ans;
string pd(string a, string b)
{
int len1 = a.length();
int len2 = b.length();
for (int i = 1; i < len1 && i < len2; i++)//i < len1 && i < len2代表不能完全包含
{
if (a.substr(len1 - i,i) == b.substr(0,i))//一个从后往前,一个从前往后,重点是substr的应用
{
return a.substr(0, len1 - i) + b;
}
}
return "fail";
}
void dfs(string drag)
{
if (drag.size() > ans)ans = drag.size();
//然后拼接这个龙
for (int i = 0; i < n; i++)
{
if (mark[i] == 2)continue;
string s = pd(drag, ss[i]);
if (s != "fail")
{
mark[i]++;
dfs(s);
mark[i]--;
}
}
}
int main()
{
cin >> n;
for (int i = 0; i < n; i++)cin >> ss[i];
char c;
cin >> c;
for (int i = 0; i < n; i++)
{
if (ss[i][0] == c)
{
mark[i]++;
dfs(ss[i]);
mark[i]--;
}
}
cout << ans;
return 0;
}
类似于迷宫的一道题(这种题一定要注意判断dfs有没有出界)
DFS
#include<iostream>
#include<cstring>
using namespace std;
const int N=30;
char g[N][N];
int n,m,cnt;
int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0};
void dfs(int x,int y)
{
g[x][y]='#';
cnt++;
for(int i=0;i<4;i++)
{
int a=x+dx[i],b=y+dy[i];
if(a<0 || a>=n || b<0 || b>=m || g[a][b]=='#') continue;
dfs(a,b);
}
}
int main()
{
while(cin>>m>>n,n||m)
{
cnt=0;
for(int i=0;i<n;i++) scanf("%s",g[i]);
int x,y,flag=0;
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
if(g[i][j]=='@')
{
x=i,y=j;
flag=1;
}
if(flag) break;
}
dfs(x,y);
cout<< cnt <<endl;
}
return 0;
}
BFS的解法
#include<iostream>
#include<cstring>
#include<queue>
#define x first
#define y second
using namespace std;
typedef pair<int,int> PII;
const int N=30;
char g[N][N];
int n,m;
int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0};
bool st[N][N];
int bfs(int x,int y)
{
int cnt=1;
queue<PII>q;
q.push({x,y});
while(!q.empty())
{
PII t=q.front();
q.pop();
int x=t.first,y=t.second;
for(int i=0;i<4;i++)
{
int a=x+dx[i];
int b=y+dy[i];
if(a<0||b<0||a>=n||b>=m)continue;
if(st[a][b])continue;
if(g[a][b]!='.')continue;
// if(g[a][b]=='.')
// {
st[a][b]=1;
cnt++;
q.push({a,b});
//
}
}
return cnt;
}
int main()
{
while(cin>>m>>n,n||m)
{
memset(st,0,sizeof st);
for(int i=0;i<n;i++) scanf("%s",g[i]);
int x,y,flag=0;
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
if(g[i][j]=='@')
{
x=i,y=j;
flag=1;
}
if(flag) break;
}
cout<< bfs(x,y) <<endl;
}
return 0;
}
奇怪的电梯-洛谷
解法一:DFS
第一种解法:暴力DFS,需要注意的是叶子结点的写法,此外ans[i]存储的是初始层到第i层的最少次数,dfs(int u,int t)的含义是,从初始层经过t次到达u层,并且当前处理的是第u层
与y总模板相似的DFS代码(剪枝)
#include<bits/stdc++.h>
using namespace std;
long long n, m, k, a[210], ans = 1e18, minn[210];
void dfs(long long u, long long v, long long cs)
{
if (u<1 || u>n)//剪枝1
return;
if (cs >= minn[u])//剪枝2
return;
if (cs >= ans)//剪枝3
return;
if (u == v)//如果搜到了
{
ans = cs;//记录答案
return;
}
minn[u] = cs;//更新到达这个点的最小次数
dfs(u + a[u], v, cs + 1);
dfs(u - a[u], v, cs + 1);
}
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define endl '\n'
#define QwQ return 0;
int main()
{
IOS;
cin >> n >> m >> k;
for (int i = 1; i <= n; i++)
cin >> a[i], minn[i] = 1e18;
dfs(m, k, 0);
if (ans != 1e18)
cout << ans;
else
cout << -1;
QwQ;
}
#include<bits/stdc++.h>
using namespace std;
const int N = 230;
int n, a, b;
int k[N],ans[N];//ans[i]=j表示从初始楼层经过j次到达i层
int cnt;
void dfs(int u, int t)
{
ans[u] = t;
//叶子结点包含在if语句里面了
if (u + k[u] <= n && t + 1 < ans[u + k[u]])dfs(u + k[u], t + 1);
if (u - k[u] >0&& t + 1 < ans[u - k[u]])dfs(u - k[u], t + 1);
}
int main()
{
cin >> n >> a >> b;
memset(ans, 0x3f, sizeof(ans));
for (int i = 1; i <= n; i++)cin >> k[i];
dfs(a,0);//从a经过0步
if (ans[b] != 0x3f3f3f3f)cout << ans[b];
else cout << "-1";
return 0;
}
解法二:BFS
BFS,有框架和模板,所有的楼层只能到一次,不能到两次
BFS模版实现电梯问题
queue<类型>Q;
Q.push(最初状态);
while(!Q.empty()){
类型 u=Q.front(); Q.pop();
for(枚举所有可扩展到的状态){
if(满足入队条件){
Q.push(状态); //维护某些必要信息
}
}
}
#define _CRT_SECURE_NO_WARNINGS 1
#include<bits/stdc++.h>
using namespace std;
const int N = 230;
int n, a, b;
int k[N];
bool vis[N];
struct node
{
int u, cnt;
};
int BFS(int a, int b)
{
queue<node>q;
q.push(node{ a,0 });
while (q.size())
{
node p = q.front();
if (p.u == b)return p.cnt;
q.pop();
//1、向上
if (p.u + k[p.u] > 0 && p.u + k[p.u] <= n && !vis[p.u + k[p.u]])
{
q.push(node{ p.u + k[p.u],p.cnt + 1 });
vis[p.u + k[p.u]] = 1;
}
//向下
if (p.u - k[p.u] > 0&&p.u - k[p.u] <= n && !vis[p.u - k[p.u]])
{
q.push(node{ p.u - k[p.u],p.cnt + 1 });
vis[p.u - k[p.u]] = 1;
}
}
return -1;
}
int main()
{
cin >> n >> a >> b;
for (int i = 1; i <= n; i++)cin >> k[i];
cout << BFS(a, b);
return 0;
}
BFS的题目练习
模版如上使用STL里面的queue
类似于迷宫最短路的一道题,上下左右四个方向是bfs的拓展条件
#include <iostream>
#include <cstring>
#include <algorithm>
#include<queue>
using namespace std;
const int N = 210;
typedef pair<int, int> PII;
char g[N][N];
int dist[N][N];
void bfs(PII start)
{
queue<PII>q;
q.push(start);
while(!q.empty())
{
PII u=q.front();q.pop();
int dx[4]={1,-1,0,0};
int dy[4]={0,0,1,-1};
for(int i=0;i<4;i++)
{
int x=u.first+dx[i];
int y=u.second+dy[i];
//进行判断
if(g[x][y]=='#')continue;
if(g[x][y]=='.')
{
dist[x][y]=dist[u.first][u.second]+1;
g[x][y]='#';
q.push({x,y});
}
if(g[x][y]=='E')
{
cout<<dist[u.first][u.second]+1<<endl;
return ;
}
}
}
cout<<"oop!"<<endl;
}
int main()
{
int t;
cin>>t;
while(t--)
{
memset(g,'#',sizeof(g));//初始化边界
memset(dist,0,sizeof(dist));
int n,m;
cin>>n>>m;
PII start;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
cin>>g[i][j];
if(g[i][j]=='S')
{
start.first=i,start.second=j,g[i][j]='#';
}
}
bfs(start);
}
return 0;
}
上面这道题的三维变形(但是没有完全过)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 200;
char g[N][N][N];
int d[N][N][N];
int l,r,c;//三个维度
struct Point
{
int z, x, y;
};
void bfs(Point start)
{
queue<Point>q;
q.push(start);
while(!q.empty())
{
Point u=q.front();q.pop();
// 六个偏移量
int dx[6] = {1, 0, -1, 0, 0, 0};
int dy[6] = {0, 1, 0, -1, 0, 0};
int dz[6] = {0, 0, 0, 0, -1, 1};
for(int i=0;i<6;i++)
{
int z=u.z+dz[i];
int x=u.x+dx[i];
int y=u.y+dy[i];
//进行判断
if(g[z][x][y]=='#')continue;
if(g[z][x][y]=='.')
{
d[z][x][y]=d[u.z][u.x][u.y]+1;
g[z][x][y]='#';
q.push({z,x,y});
}
if(g[z][x][y]=='E')
{
printf("Escaped in %d minute(s).\n", d[u.z][u.x][u.y]+1);
return ;
}
}
}
cout<<"Trapped!"<<endl;
}
int main()
{
while (cin >> l >> r >> c && l != 0)
{
memset(g,'#',sizeof g);
Point start;
for (int i = 1; i <= l; i ++)
for (int j = 1; j <= r; j ++)
for (int k = 1; k <= c; k ++)
{
cin >> g[i][j][k];
if (g[i][j][k] == 'S')
{
start.z=i,start.x=j,start.y=k;
}
}
bfs(start);
}
return 0;
}
BFS之Flood Fill
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 1010;
typedef pair<int, int> PII;
char g[N][N];
bool st[N][N];
int n;
int cnt;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
void bfs(int x,int y,int &total,int &bound)
{
st[x][y]=1;
queue<PII>q;
q.push({x,y});//创建队列并且初始化
while(!q.empty())
{
total++;//每次都要加一个
bool is_bound=0;
PII t=q.front();q.pop();//队头的取出和去除
int a=t.first,b=t.second;
for(int i=0;i<4;i++)
{
int nx=a+dx[i];
int ny=b+dy[i];
if(nx<0||ny<0||nx>=n||ny>=n)continue;//判断边界
if(st[nx][ny])continue;//是否遍历过
if(g[nx][ny]=='.'){is_bound=1;continue;}//周围有海,是边界
q.push({nx,ny});//将符合条件的坐标放入数组当中
st[nx][ny]=1;//标记当前位置
}
if(is_bound)bound++;
}
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)scanf("%s",g[i]);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
{
if(st[i][j]==0&&g[i][j]=='#')
{
int bound=0;
int total=0;
bfs(i,j,total,bound);
if(total==bound)cnt++;
}
}
cout<<cnt<<endl;
return 0;
}
BFS之填充颜色
这个加边界&处理外围的思想太赞了
题目问的是将圈内的0全部变成2,看了一篇题解,它的思路是,在读入的时候就将全部的0变成2然后BFS,将圈外的2全部变成0,一开始我还在纳闷这种BFS如果11111就是边界怎么办(因为我以为这样的BFS搜着搜着会断),没想到它直接多开了一圈 for(int i=0;i<=n+1),太赞了
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 33;
int g[N][N];
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int n;
typedef pair<int, int> PII;
void bfs(int x,int y)
{
queue<PII>q;
q.push({x,y});
while(!q.empty())
{
auto t=q.front();q.pop();
g[t.first][t.second]=0;
for(int i=0;i<4;i++)
{
int xx=t.first+dx[i],yy=t.second+dy[i];
if(xx<0||xx>n+1||yy<0||yy>n+1)continue;//注意边界的continue
if(g[xx][yy]==2)q.push({xx,yy});
}
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
cin>>g[i][j];
}
}
//太聪明了,在最外面一圈多包裹一层0,天才边界
for(int i=0;i<=n+1;i++)
for(int j=0;j<=n+1;j++)
if(g[i][j]==0)g[i][j]=2;
bfs(0,0);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
cout<<g[i][j]<<" ";
}
cout<<endl;
}
return 0;
}
带方向的BFS
E-魔法之森的蘑菇_牛客周赛 Round 37 (nowcoder.com)
开vis或者dis数组的时候多开一维记录方向。
#include<bits/stdc++.h>
using namespace std;
const int N =1010;
const int INF=1e9;
typedef pair<int,int> PII;
struct node {
int x;
int y;
int fangxiang;
};
char g[N][N];
int dis[N][N][4];
int t,n,m;
PII st;
int dx[]= {0,1,0,-1};
int dy[]= {1,0,-1,0};
int bfs(int x,int y)
{
int res=INF;
queue<node>q;
//初始化起点的四个方向
for(int i=0;i<4;i++)
{
q.push({x,y,i});
dis[x][y][i]=0;//同时起到一个vis标记数组的作用
}
while (!q.empty())
{
auto t=q.front();q.pop();
//搜索到终点
if(g[t.x][t.y]=='T')
{
res=min(res,dis[t.x][t.y][t.fangxiang]);
}
if(g[t.x][t.y]=='*'||g[t.x][t.y]=='S')
{
//可以选择以后的方向
for(int i=0;i<4;i++)
{
int nx=t.x+dx[i];
int ny=t.y+dy[i];
//判断边界条件
if(nx>=0&&nx<n&&ny>=0&&ny<m&&g[nx][ny]!='#'&&dis[nx][ny][i]==-1)
{
q.push({nx,ny,i});
dis[nx][ny][i]=dis[t.x][t.y][t.fangxiang]+1;
}
}
}
else if(g[t.x][t.y]=='.')
{
//沿着指定的方向
int i=t.fangxiang;
int nx=t.x+dx[i];
int ny=t.y+dy[i];
if(nx>=0&&nx<n&&ny>=0&&ny<m&&g[nx][ny]!='#'&&dis[nx][ny][i]==-1)
{
q.push({nx,ny,i});
dis[nx][ny][i]=dis[t.x][t.y][t.fangxiang]+1;
}
}
}
if(res>1e8)return -1;
return res;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin>>t;
while(t--) {
//初始化距离数组
memset(dis,-1,sizeof dis);
cin>>n>>m;
for(int i=0; i<n; i++) {
for(int j=0; j<m; j++) {
cin>>g[i][j];
if(g[i][j]=='S') {
st.first=i,st.second=j;
}
}
}
cout<<bfs(st.first,st.second)<<endl;
}
return 0;
}
BFS
八数码
这个题相比模板的BFS题,新增了一个难点:二维的状态表示,如何将BFS中的所有状态表示在queue队列里面,这个题也让我新学到了一点关于“压缩”的技巧。
使用unordered_map来存放字符串
作者:四谷夕雨
链接:https://www.acwing.com/solution/content/15149/
来源:AcWing
#include <iostream>
#include <algorithm>
#include <queue>
#include <unordered_map>
using namespace std;
int bfs(string start)
{
//定义目标状态
string end = "12345678x";
//定义队列和dist数组
queue<string> q;
unordered_map<string, int> d;
//初始化队列和dist数组
q.push(start);
d[start] = 0;
//转移方式
int dx[4] = {1, -1, 0, 0}, dy[4] = {0, 0, 1, -1};
while(q.size())
{
auto t = q.front();
q.pop();
//记录当前状态的距离,如果是最终状态则返回距离
int distance = d[t];
if(t == end) return distance;
//查询x在字符串中的下标,然后转换为在矩阵中的坐标
int k = t.find('x');
int x = k / 3, y = k % 3;
for(int i = 0; i < 4; i++)
{
//求转移后x的坐标
int a = x + dx[i], b = y + dy[i];
//当前坐标没有越界
if(a >= 0 && a < 3 && b >= 0 && b < 3)
{
//转移x
swap(t[k], t[a * 3 + b]);
//如果当前状态是第一次遍历,记录距离,入队
if(!d.count(t))
{
d[t] = distance + 1;
q.push(t);
}
//还原状态,为下一种转换情况做准备
swap(t[k], t[a * 3 + b]);
}
}
}
//无法转换到目标状态,返回-1
return -1;
}
int main()
{
string c, start;
//输入起始状态
for(int i = 0; i < 9; i++)
{
cin >> c;
start += c;
}
cout << bfs(start) << endl;
return 0;
}
在蓝桥杯里需要加上这两个头文件以及文件命名空间才能使用。
有时用不了unordered_map或者unordered_set时,加上
#include<tr1/unordered_set>
#include<tr1/unordered_map>
using namespace std::tr1;