一,深度搜索
1.数字的全排列
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int a[10];//这个数组中一个一个放排的
int flag[10];//这个数组标记数字是否放入
int n;
void dfs(int step){
if(step==n+1){
for(int i=1;i<=n;i++)
cout<<a[i];
cout<<endl;
}
for(int i=1;i<=n;i++){
if(flag[i]==0){
a[step]=i;
flag[i]=1;
dfs(step+1);
flag[i]=0;
}
}
}
int main(){
cin>>n;
dfs(1);
}
备注:一个数组进行标记,另一个数组进行放置的排列,一次排好以后输出排列结果;倒退回最近的一步进行选择再次进行排列。
2.二维数组寻找最少步数(走出迷宫最短步数)
(下标从1开始)
0 0 1 0
0 0 0 0
0 0 1 0
0 1 0 0
0 0 0 1
这样的一个5*4的矩阵,1代表路障,0可以走,从(1,1)出发,到(4,3)的位置
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int map[6][5];
int mins=999999;
bool flag[6][5]={false};
void dfs(int r,int c,int step){
int tr=0,tc=0;
int next[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
if(r==4&&c==3){
if(step<mins) mins=step;
return;
}
for(int k=0;k<4;k++){
tr=r+next[k][0];
tc=c+next[k][1];
if(tr>5||tr<1||tc>4||tc<1) continue;
if(flag[tr][tc]==false&&map[tr][tc]==0){
flag[tr][tc]=true;
dfs(tr,tc,step+1);
flag[tr][tc]=false;
}
}
return;
}
int main(){
for(int i=1;i<6;i++){
for(int j=1;j<5;j++){
cin>>map[i][j];
}
cout<<endl;
}
flag[1][1]=true;
dfs(1,1,0);
cout<<mins;
}
备注:与第一题不同在于 1,变成二维数组,需要开设两个二维数组,一个进行标记,另一个进行排列;2,有步数的加入,每一次递归都加一次步数,到达终点时将步数与之前步数进行比较,输出最小步数;3,分方向,循环的条件变为现在所在位置的四个方向的选择。
3.求岛屿的面积
在一张二维地图中,数字代表海拔,0代表大海,求某一个点(比如6,8)所在岛屿的面积
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int sum;
int x,y;
int map[11][11]={
0,0,0,0,0,0,0,0,0,0,0,
0,1,2,1,0,0,0,0,0,2,3,
0,3,0,2,0,1,2,1,0,1,2,
0,4,0,1,0,1,2,3,2,0,1,
0,3,2,0,0,0,1,2,4,0,0,
0,0,0,0,0,0,0,1,5,3,0,
0,0,1,2,1,0,1,5,4,3,0,
0,0,1,2,3,1,3,6,2,1,0,
0,0,0,3,4,8,9,7,5,0,0,
0,0,0,0,3,7,8,6,0,1,2,
0,0,0,0,0,0,0,0,0,1,0
};
bool flag[11][11]={false};
void dfs(int r,int c){
int next[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
int tr=0,tc=0;
for(int i=0;i<4;i++){
tr=r+next[i][0];
tc=c+next[i][1];
if(tr>10||tr<1||tc>10||tc<1) continue;
if(map[tr][tc]>0&&flag[tr][tc]==false){
sum++;
flag[tr][tc]=true;
dfs(tr,tc);
}
}
return;
}
int main(){
cin>>x>>y;
flag[x][y]=true;
if(map[x][y]<=0){
sum=0;cout<<sum;
return 0;
}
else sum=1;
dfs(x,y);
cout<<sum;
}
备注:岛屿的面积,在dfs(tr,tc)之后,不需要将该点置于false,否则就变为路径的搜索。
4.岛屿的数量
上题中有多少个岛屿
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int num=0;
int map[11][11]={
0,0,0,0,0,0,0,0,0,0,0,
0,1,2,1,0,0,0,0,0,2,3,
0,3,0,2,0,1,2,1,0,1,2,
0,4,0,1,0,1,2,3,2,0,1,
0,3,2,0,0,0,1,2,4,0,0,
0,0,0,0,0,0,0,1,5,3,0,
0,0,1,2,1,0,1,5,4,3,0,
0,0,1,2,3,1,3,6,2,1,0,
0,0,0,3,4,8,9,7,5,0,0,
0,0,0,0,3,7,8,6,0,1,2,
0,0,0,0,0,0,0,0,0,1,0
};
bool flag[11][11]={false};
void dfs(int r,int c,int n){
int next[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
int tr=0,tc=0;
map[r][c]=n;
for(int i=0;i<4;i++){
tr=r+next[i][0];
tc=c+next[i][1];
if(tr>10||tr<1||tc>10||tc<1) continue;
if(map[tr][tc]>0&&flag[tr][tc]==false){
flag[tr][tc]=true;
dfs(tr,tc,n);
}
}
return;
}
int main(){
for(int i=0;i<11;i++){
for(int j=0;j<11;j++){
if(map[i][j]>0){
num--;
flag[i][j]=true;
dfs(i,j,num);
}
}
}
cout<<-num;
}
备注: 从左上角开始遍历寻找,找到一个岛将它置于num--(并深度搜索与该岛屿相连的所有岛都置于该num--),相当于划分板块,-1,-2,-3...板块,最后一个板块的负即为岛屿的个数了。
5.方格填数
如下的10个格子
填入0~9的数字。要求:连续的两个数字不能相邻。
(左右、上下、对角都算相邻)
一共有多少种可能的填数方案?
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int sum=0;
int num[3][4];
bool isfilled[10];
bool islegal(int r, int c, int val)
//第一排检查左右,第一列检查上和右上,第三列检查上,上左,左,其余检查左,上左,上,上右;
{
if( r == 0 )
return abs(val-num[r][c-1]) > 1;
else if( c == 0 )
return abs(val-num[r-1][c]) > 1 && abs(val-num[r-1][c+1])>1;
else if( c != 3 )
return abs(val-num[r][c-1]) > 1 && abs(val-num[r-1][c]) > 1 && abs(val-num[r-1][c-1])>1 && abs(val-num[r-1][c+1])>1;
else
return abs(val-num[r][c-1]) > 1 && abs(val-num[r-1][c]) > 1 && abs(val-num[r-1][c-1])>1;
}
void DFS(int r,int c){
if(r==2&&c==3){
sum++;
return ;
}
for(int i=0;i<10;i++){
if(!isfilled[i]&&islegal(r,c,i)){
num[r][c]=i;
isfilled[i]=true;
DFS(r+(c+1)/4,(c+1)%4);
// if(c==3) DFS(r+1,0);
// else DFS(r,c+1);
isfilled[i]=false;
}
}
}
int main(){
num[0][0]=-10;
num[2][3]=-10;
for(int i=0;i<10;i++){
isfilled[i]=false;
}
DFS(0,1);
cout<<sum;
return 0;
}
6.寒假作业
每个方块代表1~13中的某一个数字,但不能重复。
比如:
6 + 7 = 13
9 - 8 = 1
3 * 4 = 12
10 / 2 = 5
以及:
7 + 6 = 13
9 - 8 = 1
3 * 4 = 12
10 / 2 = 5
就算两种解法。(加法,乘法交换律后算不同的方案)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int a[4][3];
int flag[14]={false};
int sum=0;
bool islegal(int r,int i){
if(r==0){
return a[r][0]+a[r][1]==i;
}
else if(r==1){
return a[r][0]-a[r][1]==i;
}
else if(r==2){
return a[r][0]*a[r][1]==i;
}
else if(r==3){
return a[r][1]*i==a[r][0];
}
else return false;
}
void dfs(int r,int c){
if(r==4){
sum++;
return;
}
for(int i=1;i<14;i++){
if(!flag[i]){
flag[i]=true;
if(c!=2||islegal(r,i)){
a[r][c]=i;
dfs(r+(c+1)/3,(c+1)%3);
}
flag[i]=false;
}
}
}
int main(){
dfs(0,0);
cout<<sum;
}
7.方格分割
6x6的方格,沿着格子的边线剪开成两部分。
要求这两部分的形状完全相同。
如图:p1.png, p2.png, p3.png 就是可行的分割法
试计算:
包括这3种分法在内,一共有多少种不同的分割方法。
注意:旋转对称的属于同一种分割法
//以线进行分割,0-6;
#include <iostream>
#include <algorithm>
#include <set>
#include <queue>
#include <cstring>
#define MAX 100000
using namespace std;
int map[7][7];
bool flag[7][7]={false};
int ans=0;
int next[4][2]={0,1,0,-1,1,0,-1,0};
void dfs(int r,int c){
if(r==0||c==0||r==6||c==6){
ans++;
return;
}
for(int i=0;i<4;i++){
int tr=r+next[i][0];
int tc=c+next[i][1];
// if(tr<0||tc<0||tr>6||tc>6) continue;
if(flag[tr][tc]==false){
flag[tr][tc]=true;//
flag[6-tr][6-tc]=true;
dfs(tr,tc);
flag[tr][tc]=false;
flag[6-tr][6-tc]=false;
}
}
}
int main(){
flag[3][3]=true;
dfs(3,3);
cout<<ans/4;
}
8.全球变暖
//蓝桥 ,用深搜写 太累了 ,先贴上,后补写广搜
#include <iostream>
#include <algorithm>
#include <set>
#include <queue>
#include <cstring>
#define MAX 100
using namespace std;
int n;
int map[MAX][MAX];
bool flag[MAX][MAX]={false};
void dfs(int r,int c,int val){
int next[4][2]={0,1,0,-1,1,0,-1,0};
int tr=0,tc=0;
map[tr][tc]=val;
for(int i=0;i<4;i++){
tr=r+next[i][0];
tc=c+next[i][1];
if(tr<0||tc<0||tr>=n||tc>=n) continue;
if(map[tr][tc]>1&&flag[tr][tc]==false) dfs(tr,tc,val);
}
}
int main(){
cin>>n;
char ch;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cin>>ch;
if(ch=='.') map[i][j]=0;
else map[i][j]=1;
}
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(map[i][j]==1){
if(map[i-1][j]==1&&map[i+1][j]==1&&map[i][j+1]==1&&map[i][j-1]==1)
map[i][j]=2;
}
}
}
int ans=0;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(map[i][j]>1){
ans--;
flag[i][j]=true;
dfs(i,j,ans);
}
}
}
cout<<-ans;
// for(int i=0;i<n;i++){
// for(int j=0;j<n;j++){
// cout<<map[i][j];
// }
// cout<<endl;
// }
}
这两个博客写的很好,我看得第一个文章介绍,做的第二个题目。
二、广度搜索
1.在一个n*m(n行, m列)的迷宫中,存在着一个入口、一些墙壁以及一个宝藏。由于迷宫是四连通的,即在迷宫中的一个位置,只能走到与它直接相邻的其他四个位置(上、下、左、右)。现洪尼玛在迷宫的入口处,问他最少需要走几步才能拿到宝藏?若永远无法拿到宝藏,则输出-1。
输入样例1:
3 3
S.#
.#.
.#E
输出 -1
输入样例2:
5 5
S.#..
#.#.#
#.#.#
#...E
#....
输出 7
#include <iostream>
#include <algorithm>
#include <set>
#include <queue>
#define MAX 101
using namespace std;
char map[MAX][MAX];
bool vis[MAX][MAX]={false};//标记是否已经访问
struct point{//结构体存储该点的信息情况
int r,c,num;
point(int rr,int cc,int nn){
r=rr;
c=cc;
num=nn;//搜索的范围(逐步扩大)
}
};
int next[4][2]={1,0,-1,0,0,1,0,-1};
int flag,val,num=0;
int n,m;
queue<point> q;//用赌队列进行维护
void bfs(int sr,int sc){
q.push(point(sr,sc,0));
vis[sr][sc]=true;
while(!q.empty()){
point p=q.front();//p存储队列第一位的信息
q.pop();
if(map[p.r][p.c]=='E'){
val=p.num;
flag=1;
break;
}
for(int i=0;i<4;i++){
int tr=p.r+next[i][0];
int tc=p.c+next[i][1];
if(tr<0||tc<0||tr>=n||tc>=m) continue;//不可越界
if(map[tr][tc]!= '#'&&vis[tr][tc]==false){//判断条件
q.push(point(tr,tc,p.num+1));
vis[tr][tc]=true;
}
}
}
}
int main(){
flag=0;
int r1,c1;
cin>>n>>m;
for(int i=0;i<n;i++){//赋值
cin>>map[i];
}
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(map[i][j]=='S'){//标记起点
r1=i;
c1=j;
}
}
}
bfs(r1,c1);
if(flag==0) cout<<"-1";
else cout<<val;
return 0;
}