前言:
只有题目和代码及其部分注释,参考代码自行思考;题目在代码段和题目大意中均有解释,部分测试样例也在代码段;我会在下面给出DFS和BFS基本框架和基本考虑的方向,仅供参考,具体题目具体分析,咱就是说,要随机应变,当然,例题只是作为辅助思考并且都是些特别特别基础的DFS和BFS练习;
基本框架和思考角度:
//BFS和DFS基本框架
//BFS:
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn = 100;
bool vst[maxn][maxn]; // 访问标记
int dir[4][2] = { 0,1,0,-1,1,0,-1,0 }; // 方向向量
struct State // BFS 队列中的状态数据结构
{
int x, y; // 坐标位置
int Step_Counter; // 搜索步数统计器
};
State a[maxn];
bool CheckState(State s) // 约束条件检验
{
if (!vst[s.x][s.y] && ...) // 满足条件
return 1;
else // 约束条件冲突
return 0;
}
void bfs(State st)
{
queue <State> q; // BFS 队列
State now, next; // 定义2 个状态,当前和下一个
st.Step_Counter = 0; // 计数器清零
q.push(st); // 入队
vst[st.x][st.y] = 1; // 访问标记
while (!q.empty())
{
now = q.front(); // 取队首元素进行扩展
if (now == G) // 出现目标态,此时为Step_Counter 的最小值,可以退出即可
{
// ...... // 做相关处理
return;
}
for (int i = 0; i < 4; i++)
{
next.x = now.x + dir[i][0]; // 按照规则生成 下一个状态
next.y = now.y + dir[i][1];
next.Step_Counter = now.Step_Counter + 1; // 计数器加1
if (CheckState(next)) // 如果状态满足约束条件则入队
{
q.push(next);
vst[next.x][next.y] = 1; //访问标记
}
}
q.pop(); // 队首元素出队
}
return;
}
int main()
{
//......
return 0;
}
/***********************************************************************/
//DFS:
//DFS:
/*
该DFS 框架以2D 坐标范围为例,来体现DFS 算法的实现思想。
*/
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
const int maxn = 100;
bool vst[maxn][maxn]; // 访问标记
int map[maxn][maxn]; // 坐标范围
int dir[4][2] = { 0,1,0,-1,1,0,-1,0 }; // 方向向量,(x,y)周围的四个方向
bool CheckEdge(int x, int y) // 边界条件和约束条件的判断
{
if (!vst[x][y] && ...) // 满足条件
return 1;
else // 与约束条件冲突
return 0;
}
void dfs(int x, int y)
{
vst[x][y] = 1; // 标记该节点被访问过
if (map[x][y] == G) // 出现目标态G
{
//...... // 做相应处理
return;
}
for (int i = 0; i < 4; i++)
{
if (CheckEdge(x + dir[i][0], y + dir[i][1])) // 按照规则生成下一个节点
dfs(x + dir[i][0], y + dir[i][1]);
}
return; // 没有下层搜索节点,回溯
}
int main()
{
// ......
return 0;
}
第一题:经典扑克牌全排列问题
经典扑克牌全排列问题:A(n,n)即可
//ABC
//假如有编号为1,2,3的3张扑克牌
//和编号为1,2,3的3个盒子。
//将这3张扑克牌分别放入3个盒子
//一共有几种不同的放法呢?
//提示:全排列
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
#include <queue>
#include <vector>
#include <cmath>
#include <set>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
const int N = 1e5 + 10;
int n,index;
int arr[N];
bool book[N];
void dfs(int index)
{
if(index==n+1)
{
for(int i=1;i<=n;++i)
cout<<arr[i];
cout<<endl;
}
for(int i=1;i<=n;++i)
{
if(book[i]==0)
{
arr[index]=i;
book[i]=1;
dfs(index+1);
book[i]=0;
}
}
}
void solve()
{
cin>>n;
dfs(1);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
// int T;cin >> T;
// while ( T -- )
solve();
}
第二题:统计1-9 9个数字两个数字和等于另外一个数字
题目大意就是:
统计1-9 9个数字,每三个一组组成一个数字,两个数字和等于另外一个数字;
数字不可以重复使用,请你求出一共有多少种组合
eg:718+245=963 658+314=972 378+216=594 这些情况都可以
没有输入,只要输出最终结果即可
//1-9 9个数凑一个等式,其中每个数由3个数字组成
//统计有多少种可能
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
#include <queue>
#include <vector>
#include <cmath>
#include <set>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
const int N = 1e5 + 10;
int n,index,cnt=0;
int arr[10];
bool book[10];
void dfs(int index) {
if(index==10) {
int a=arr[1]*100+arr[2]*10+arr[3];
int b=arr[4]*100+arr[5]*10+arr[6];
int c=arr[7]*100+arr[8]*10+arr[9];
if(a+b==c) {
cout<<a<<"+"<<b<<"="<<c<<endl;
cnt++;
return;
}
}
for(int i=1; i<10; ++i) {
if(book[i]==0) {
arr[index]=i;
book[i]=true;
dfs(index+1);
book[i]=false;
}
}
}
void solve() {
dfs(1);
cout<<cnt/2<<endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
// int T;cin >> T;
// while ( T -- )
solve();
}
第三题:迷宫
题目大意:
处于迷宫入口的小明(1,1), 寻找位于(p,q)的小红,也就是最短路径问题 ,其中n表示行,m表示列,迷宫用二维数组表示即可,其他表示方法也可以(测试用例在代码中有)
下面这副图转载《啊哈算法》书中插图
/*
迷宫用二维数组表示
question:
处于迷宫入口的小明(1,1),
寻找位于(p,q)的小红,
也就是最短路径问题
其中n表示行,m表示列
测试用例:
5 4
0 0 1 0
0 0 0 0
0 0 1 0
0 1 0 0
0 0 0 0
1 1
3 4
cout<<:
5
*/
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
#include <queue>
#include <vector>
#include <cmath>
#include <set>
using namespace std;
#define endl '\n'
typedef pair<int, int> PII;
const int N = 1e5 + 10;
int n, m, index, min_step = 999999;
int startx, starty;//初始位置
int p, q;//小红坐标
int arr[1000][1000];//地图
bool book[1000][1000];//判别数组
int next_step[4][2]{//方向数组
{0,1},
{1,0},
{0,-1},
{-1,0},
};
void dfs(int x, int y, int step) {
int tx, ty, k;
//退出条件
if (x == p && y == q) {
if (step < min_step)
min_step = step;
return;
}
for (int i = 0; i <= 3; ++i)
{
tx = x + next_step[i][0];
ty = y + next_step[i][1];
if (tx > n || tx < 1 || ty<1 || ty>m)continue;
if (arr[tx][ty] == 0 && book[tx][ty] == false)
{
book[tx][ty] = true;
dfs(tx, ty, step + 1);
book[tx][ty] = false;
}
}
return;
}
void solve() {
//迷宫大小
cin >> n >> m;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
cin >> arr[i][j];//输入地图
cin >> startx >> starty;
cin >> p >> q;
book[startx][starty] = true;
dfs(startx, starty, 0);
cout << min_step << endl;//最短路
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
// int T;cin >> T;
// while ( T -- )
solve();
}
第四题:贪吃蛇大作战
Attention:
两个人走的路径相反,所以也只需要一个方向指标,加一个负号就可以,因为两者方向相反,还有就是要注意dfs递归后不需要标记book使其变成false,因为你的选择路径已经确定了,不可改变!!
#include<iostream>
#include<queue>
#include<set>
#include<string>
#include<numeric>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<map>
#define f(i,n,m) for(long long i=n;i<=m;++i)
#define unf(i,n,m) for(long long i=n;i>=m;--i)
#define int long long
const int N = 2e5 + 10;
using namespace std;
const int MaxN = 2e5 + 5;
typedef pair<int, int> PII;
int arr[100][100];
bool book[100][100];
int n;
int x1 = 1, y_1=1 ;
int x2 , y2 ;
int dx[4] = { 1,-1,0,0 };
int dy[4] = { 0,0,1,-1 };
int pos;
void dfs(int x1, int y_1, int x2, int y2, int fx)
{
if (x1 == x2 && y_1 == y2)
{
return;
}
for (int i = 0; i < 3; ++i)
{
int tx1 = x1 + dx[(fx + i) % 4];
int ty1 = y_1 + dy[(fx + i) % 4];
int tx2 = x2 - dx[(fx + i) % 4];
int ty2 = y2 - dy[(fx + i) % 4];
if (tx1<1 || tx1>n || ty1<1 || ty1>n || tx2<1 || tx2>n || ty2<1 || ty2>n)
continue;
if (book[tx1][ty1] == false && book[tx2][ty2] == false)
{
book[tx1][ty1] = true;
book[tx2][ty2] = true;
arr[tx1][ty1] = ++pos;
arr[tx2][ty2] = pos;
dfs(tx1, ty1, tx2, ty2, (fx + i)%4);
}
}
}
void solves() {
memset(arr, 0, sizeof(arr));
memset(book, false, sizeof(book));
pos = 0;
x2 = y2 = n;
book[1][1] = book[n][n] = true;
arr[x1][y_1] = ++pos;
arr[x2][y2] = pos;
dfs(1, 1, n, n, 0);
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j < n; ++j)
{
cout << arr[i][j] << " ";
}
cout << arr[i][n];
cout << endl;
}
cout << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
while (cin >> n)
solves();
return 0;
}
第五题:走迷宫
大意:
输入n,得到n*n地图(二维数组开起来就可以了),在输入x,y为起始位置,xx1,yy1为终点,那么从初始位置到最终位置最少需要多少步?
eg:
BFS常用于迷宫,即从一点到另一点的最短路径,所以这里以一个迷宫来说明:
这里有一个5*5的迷宫,1是墙,0是路,那么从左上角走道右下角最少需要多少步呢?
0 1 0 0 0 对于数组第一个下标为(0,0)
0 1 0 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0 起始位置(0,0),末位置(4,4,)最短路8步
#include<bits/stdc++.h>
using namespace std;
using PII=pair<int,int>;
int k,n,xx1,yy1;
int dx[4]={1,-1,0,0};
int dy[4]={0,0,1,-1};
int map1 [1000][1000];
bool book[1000][1000];
struct node{
int step;
int x,y;
}now,nextt;
queue<node>q;
int BFS(int x,int y)
{
int xx, yy;
map1[x][y]=2;
now.x=x;
now.y=y;
now.step=0;
q.push(now);//now
while(!q.empty())
{
now=q.front();
q.pop();
for(int i=0;i<=3;++i)
{
xx=now.x+dx[i];
yy=now.y+dy[i];
if(xx>=0&&xx<n&&yy>=0&&yy<n&&map1[xx][yy]!=2&&map1[xx][yy]!=2)
{
nextt.x=xx;
nextt.y=yy;
nextt.step=now.step+1;
map1[now.x][now.y]=2;
if(xx==xx1&&yy==yy1)
return nextt.step;
q.push(nextt);
}
}
}
return -1;
}
void solves()
{
cin>>n;
int x,y;
for(int i=0;i<n;++i)
{
for(int j=0;j<n;++j)
{
cin>>map1[i][j];
}
}
cin>>x>>y>>xx1>>yy1;
int ans=BFS(x,y);
cout<<ans<<endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
solves();
return 0;
}
第六题:16蓝桥杯填网格问题
2016年第七届蓝桥杯省赛真题
#include<queue>
#include<iostream>
#include<string.h>
#include<cstdio>
#include<cstring>
#include<numeric>
using namespace std;
#define int long long
typedef pair<int,int> PII;
int n,m,cnt=0;
int dx[8]= {1,1,1,-1,-1,-1,0,0};
int dy[8]= {-1,1,0,0,-1,1,1,-1};
char map1[1000][1000];
bool book[100][100];
int vis[100];
struct node {
int x,y,step;
} now,nextt;
void Init() {
for(int i=0; i<=5; ++i) {
for(int j=0; j<6; ++j) {
map1[i][j]=-100;
}
}
}
/*bool check(int i,int j){ //¼ì²é9¹¬¸ñ
for(int x=i-1;x<=i+1;x++){
for(int y=j-1;y<=j+1;y++){
if(abs(a[x][y]-a[i][j])==1)
return false;
}
}
return true;
}*/
bool Check(int x,int y) {
for(int i=x-1; i<x+1; ++i) {
for(int j=y-1; j<=y+1; ++j) {
if(abs(map1[x][y]-map1[i][j])==1)
return false;
}
}
return true;
}
void DFS(int x,int y) {
if(x==3&&y==4) {//出口3,4 因为边框还围着一群-100;
cnt++;
return;
}
for(int i=0; i<10; ++i) {
if(vis[i]==0)
{
map1[x][y]=i;
if(Check(x,y)){
vis[i]=1;
if(y==4)
DFS(x+1,1);
else
DFS(x,y+1);
vis[i]=0;
map1[x][y]=-100;
} else {
map1[x][y]=-100;
continue;
}
}
}
}
void Solves() {
Init();
DFS(1,2);
cout<<cnt<<endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
/* cin>>t;
while(t--)*/
Solves();
return 0;
}
考察dfs深搜,把所有可能记录下来,最终结果为1580;
第七题:17蓝桥杯 方格分割
2017蓝桥杯省赛真题-方格分割
#include<queue>
#include<iostream>
#include<string.h>
#include<cstdio>
#include<cstring>
#include<numeric>
using namespace std;
#define int long long
typedef pair<int,int> PII;
int n,m,cnt=0;
int dx[4]= {0,0,1,-1};
int dy[4]= {-1,1,0,0};
int map1[1000][1000];
bool book[100][100];
int vis[100];
struct node {
int x,y,step;
} now,nextt;
void DFS(int x,int y)
{
if(x<=0||x>=6||y<=0||y>=6)
{
cnt++;
return ;
}
for(int i=0;i<=3;++i)
{
int xx=x+dx[i];
int yy=y+dy[i];
if(map1[xx][yy]==0&&map1[6-xx][6-yy]==0)
{
map1[xx][yy]=1;
map1[6-xx][6-yy]=1;
DFS(xx,yy);
map1[xx][yy]=0;
map1[6-xx][6-yy]=0;
}
}
}
void Solves() {
map1[3][3]=1;
DFS(3,3);
cout<<cnt/4<<endl;
//509
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
/* cin>>t;
while(t--)*/
Solves();
return 0;
}
思路:从中间出发(3,3),向两边走,形成一条切割线。然后因为是旋转体(旋转4次回到起始形状),所以要除以4,
为什么从(3,3)呢???
答:因为map1【3,3】==map【6-3】【6-3】为对称中心点
第八题:Lake Counting s
题源:洛谷
#include<queue>
#include<iostream>
#include<string.h>
#include<cstdio>
#include<cstring>
#include<numeric>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
#define heap priority_queue
#define pll pair<long long, long long>
#define fir first
#define sec second
#define SPO(n) fixed << setprecision(n)
#define FOR(i, l, r) for (long long i = l; i <= r; ++i)
#define ROF(i, r, l) for (long long i = r; i >= l; --i)
#define endl '\n'
#define DBG(n) cout << "!!!" << #n << ": " << n << endl
const int INTINF = 0x3f3f3f3f;
const long long INF = 0x3f3f3f3f3f3f3f3f;
const double EPS = 1e-9;
const long long MAX = 1e18 + 5;
int n,m;
char map1[105][105];
int cnt;
int dx[8]={0,0,1,1,-1,-1,-1,1};
int dy[8]={1,-1,1,-1,-1,1,0,0};
bool Check(int x,int y)
{
for(int i=x-1;i<=x+1;++i)
{
for(int j=y-1;j<=y+1;++j)
{
if(map1[i][j]=='W')
return false;
}
}
return true;
}
void DFS(int x,int y)
{
map1[x][y]='.';
for(int i=0;i<8;++i)
{
int xx=x+dx[i];
int yy=y+dy[i];
if(map1[xx][yy]=='W')
{
map1[xx][yy]='.';
DFS(xx,yy);
}
}
}
void Solve()
{
cin>>n>>m;
for(int i=0;i<n;++i)
{
cin>>map1[i];
}
for(int i=0;i<n;++i)
{
for(int j=0;j<m;++j)
{
if(map1[i][j]=='W')
{
cnt++;
DFS(i,j);
}
}
}
cout<<cnt<<endl;
return;
}
int main(void)
{
int t;
/* cin>>t;
while(t--)*/
Solve();
return 0;
}
第九题:剪邮票
2016第七届蓝桥杯省赛真题
#include<queue>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<cstdio>
#include<string>
#include<cstring>
#include<numeric>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
#define heap priority_queue
#define pll pair<long long, long long>
#define fir first
#define sec second
#define SPO(n) fixed << setprecision(n)
#define FOR(i, l, r) for (long long i = l; i <= r; ++i)
#define ROF(i, r, l) for (long long i = r; i >= l; --i)
#define endl '\n'
#define DBG(n) cout << "!!!" << #n << ": " << n << endl
const int INTINF = 0x3f3f3f3f;
const long long INF = 0x3f3f3f3f3f3f3f3f;
const double EPS = 1e-9;
const long long MAX = 1e18;
int gag[3][4];
int ans,n;
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
void dfs(int x,int y)
{
gag[x][y]=0;
for(int i=0;i<4;++i)
{
int xx=x+dx[i];
int yy=y+dy[i];
if(xx>=0&&xx<3&&yy>=0&&yy<4&&gag[xx][yy]==1)
{
gag[xx][yy]=0;
dfs(xx,yy);
}
}
return ;
/* gag[x][y]=0;
if(x-1>=0&&gag[x-1][y]==1)dfs(x-1,y);
if(x+1<3&&gag[x+1][y]==1)dfs(x+1,y);
if(y-1>=0&&gag[x][y-1]==1)dfs(x,y-1);
if(y+1<4&&gag[x][y+1]==1)dfs(x,y+1);*/
}
bool Check(int a[])
{
//一维数组转二维
for(int i=0;i<3;++i)
{
for(int j=0;j<4;++j)
{
if(a[i*4+j]==1)gag[i][j]=1;
else gag[i][j]=0;
}
}
//dfs连通性检测
int cnt=0;
for(int i=0;i<3;++i)
{
for(int j=0;j<4;++j)
{
if(gag[i][j]==1)
{
dfs(i,j);
cnt++;
}
}
}
return cnt==1;//等于就就可以,否则说明不连通
}
void Solve()
{
int a[12]={0,0,0,0,0,0,0,1,1,1,1,1,};
do{
if(Check(a))
ans++;
}while(next_permutation(a,a+12));//全排列
cout<<ans<<endl;
return;
}
int main(void)
{
int t;
/* cin>>t;
while(t--)*/
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
/* cin>>t;
while(t--)*/
Solve();
return 0;
}
题意:
看到这个题目,便很容易想到全排列,因为要剪5张连在一起的邮票,则可以用一个数组a[12]来标记,其中5个数为1,其余全为0。
这里全排列推荐使用next_permutation
,可以去除重复的排列方式,而用递归回溯的方法还要考虑去重。
这里会了解到全排列去重的两个函数:next_permutation(a,a+12)andprev_permutation(a,a+12)
第十题:八数码 问题
#include <bits/stdc++.h>
#include<unordered_map>
using namespace std;
#define Fast std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define SQR(i) fixed<<setprecision(i)
#define int long long
typedef long long ll;
#define endl '\n'
#define int long long
#define inf 0x3f3f3f3f
#define inf_max 0x7f7f7f7f
typedef pair<int, int> PII;
const int N = 2e6 + 5;
int bfs(string& s)
{
int dx[] = { 1,-1,0,0 };
int dy[] = { 0,0,1,-1 };
queue<string>q;//存下一步的队列
string st= "12345678x";//目标串
unordered_map<string, int>dis;//记录路径到达这样的str的距离
q.push(s);
dis[s] = 0;//初始为0
while (!q.empty())
{
auto t = q.front();
q.pop();
int distance = dis[t];//记录当前步数
if (t == st)return distance;//找到了返回当前步数
int k = t.find('x');//查找x的下标位置
int x = k / 3, y = k % 3;
//常用的一维转二维的公式
for (int i = 0; i < 4; ++i)
{
int xx = x + dx[i];
int yy = y + dy[i];
//遍历四个方向
if (xx >= 0 && xx < 3 && yy >= 0 && yy < 3)
{
swap(t[xx * 3 + yy], t[x * 3 + y]);
//使得str发生改变 即走过后的位置
if (!dis[t])q.push(t), dis[t] = distance + 1;
//如果他没有走过,记录下来 步数+1 之前走过就不需要再记录了
//因为我们已经有过更短的了
swap(t[xx * 3 + yy], t[x * 3 + y]);
//复原之前的字符串为下一次循环做准备
}
}
}
return -1;
//没有找到就返回-1
}
void Solve() {
string str = "";
for (int i = 0; i < 9; ++i)
{
char ch;
cin >> ch;
str += ch;
}
cout << bfs(str) << endl;
}
signed main() {
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
Solve();
}