算法搜索DFS BFS的学习和刷题

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

 全排列的拓展题目

1209. 带分数 - AcWing题库

#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

考前临时抱佛脚

kkksc03考前临时抱佛脚 - 洛谷

每一组的左半边和右半边的暴力搜索

#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语言可以省去很多的代码

3502. 不同路径数 - AcWing题库

这个题的dfs的参数dfs(i,j,u,num) 分别是当前点的坐标+当前已经有的位数+当前生成的数的值

使用unordered_set<int>S;

C++总结(7):STL无序容器之unordered_set、unordered_map、unordered_multiset、unordered_multimap详解-CSDN博客

这个题的代码如下 

#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题 

 活动 - AcWing

#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

主要是积累做题经验,将题目中的关键信息提炼出来

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有没有出界)

1113. 红与黑 - AcWing题库

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的拓展条件

1101. 献给阿尔吉侬的花束 - AcWing题库

#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

1233. 全球变暖 - AcWing题库

#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

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

八数码

活动 - AcWing

这个题相比模板的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;

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值