文章目录

  • 搜索算法及练习
    • 1. 搜索算法概述
    • 2. 宽度优先搜索(Breadth First Search,BFS)
      • 2.1 Robot in Maze
      • 2.2 Yet Another Multiple Problem
      • 2.3 马的遍历
      • 2.4 Fire!
    • 3. 深度优先搜索(Depth First Search,DFS)
      • 3.1 八皇后问题
      • 3.2 迷宫
      • 3.3 奇怪的电梯
      • 3.4 [NOIP2017 提高组] 奶酪
      • 3.5 [USACO05DEC]Scales S
      • 3.6 油田(连通块)
    • 4. 剪枝
      • 4.1 小木棍 [数据加强版]
      • 4.2 [NOIP2004 提高组] 虫食算(数论+搜索)
    • 5. A*算法
      • 5.1 八数码难题(哈希+搜索)
    • 6. 迭代加深搜索
      • 6.1 快速幂计算 Power Calculus
    • 7. 双向宽度优先搜索
      • 7.1 八数码难题
    • 8. 舞蹈链

搜索算法及练习

1. 搜索算法概述

​ 搜索是一种通过穷举所有可能解的状态,来求得题目所要求的解或者最优解的方法,即通过枚举解的所有可能状态,来寻求一个解或者最优解。在这个过程中,必须确保不会重复搜索已经搜索过的状态,否则会导致循环的产生。同时也要考虑总的状态数是否在可接受的范围内,否则会导致超时或者算法无法停止。

​ 然而有些时候,看起来因为状态数太多而无法进行搜索的题目也可以通过各种剪枝的方法求出解。所谓剪枝,就是在搜索过程中,有意地避开那些虽然也属于可达状态,但是绝对不会是所求解的情况,通过这种方式减少总的所需搜索的状态数,来达到在时限要求内求出解的目的。

​ 想要用搜索算法解题,首先,需要表达出题目的状态空间,即题目所描述的初始状态、可达状态和终结状态,并确定它们之间的转移条件。其次,需要提出一个合理的搜索方式,这个搜索方式必须保证可以到达所有可能的情况,并且不会导致死循环的出现。最后,需要估计这样做是否能在题目所给定的时间和空间限定内解出答案,如果超过了时间和空间限定,是否可以通过剪枝或者改变搜索方式等方法来优化算法,以达到符合限制条件的目的。

​ 在搜索的时候,需要先建立一种搜索的顺序,来保证每一个状态都被搜索到且仅搜索到一次。常用的搜索顺序有两种:宽度搜索深度优先搜索

2. 宽度优先搜索(Breadth First Search,BFS)

概述

​ 宽度优先搜索遍历类似于树的按层次遍历的过程。假设从图中某一点1出发,发现1可以到达2,此时记录下2这个顶点,但是并不从2继续搜索,而是依然寻找1可以到达的点3、4、5…直到所有1可以到达的点都被记录下来,再寻找最早被记录的点,即2,从它开始重新按照1的方式进行搜索,但是对于已经搜索到的点(如1,3)则不再记录,只记录只有2可以到达的之前没有到达过的点即可。

​ 对于《ACM\ICPC算法基础训练教程》P100页中的无向图G进行宽度优先搜索遍历,首先访问1和1的邻接点2和3,然后依次访问2的邻接点4和5以及3的邻接点6和7,最后访问4的邻接点8.由于这些顶点的邻接点均已被访问,并图的所有顶点都被访问,由此完成了图的遍历。得到的顶点访问序列为:
1→2→3→4→5→6→7→81\rightarrow2\rightarrow3\rightarrow4\rightarrow5\rightarrow6\rightarrow7\rightarrow8 1→2→3→4→5→6→7→8
​ 在搜索遍历的过程中,需要使用数组visit[maxn]记录某个点是否在之前已经到达,同时,为了按照记录的顺序访问点,需要使用队列来记录所有第一次到达的点。

​ 从图的某一顶点v出发,递归地进行宽度优先遍历的伪代码:

void BFS(){for(v=0;v<maxn;v++)visit[v]=false;q.clear();q.push(start); /* 放入队头 */while(!q.empty()){u=q.front();q.pop();   /* 队头元素出队并置为u */visited[u]=true;visit(u);if(!visited[w]){q.push(w);    /* 对于每个和u相邻的元素w的尚未访问的邻接结点w入队列q */}}
}

通过观察宽度优先搜索可以发现,因为visit数组的存在,每一个点最多只会进入队列一次,因此,遍历图的过程实质上是对每个顶点查找其相邻点的过程。其时间复杂度则取决于对应的存储结构。若使用邻接矩阵,那么算法所需时间复杂度为O(n2)O(n^2)O(n2),而当以邻接表作为图的存储结构时,所需时间为O(n+m)O(n+m)O(n+m)。

2.1 Robot in Maze

题目描述:有一个机器人被困在一个迷宫中,请你告诉它如何到达目的地。

​ 迷宫是一个M×NM\times NM×N的矩阵,有一些是空的,还有一些被墙填满。机器人不能移动到被墙填满的格子里,也不能移动到矩阵外。机器人只能接受三个操作:向左转、向右转和前进。机器人开始的时候面朝北。输入一个矩阵,用#代表墙,代表空,S代表机器人的起始点,T代表机器人的目的地。输出让机器人到达目的地的最小操作数。

​ 输入的第一行表示样例数,下面的一行有两个数,分别表示迷宫的MMM和NNN,下面的MMM行每行有NNN个符号,表示整个迷宫。1≤M,N≤1001\leq M,N \leq 1001≤M,N≤100。

​ 输出只有一行,即最小操作数。

输入样例

2
5 5
#####
#...#
#.#.#
#S#T#
#####
4 5
#.#.#
#.#.#
#S#T#
#####

输出样例

8
-1

题目解析

​ 题目要找最少的操作数,可以用BFS遍历的方法进行搜索,即从起点开始进行BFS,对每一个新到达的位置记录当前到达的时间,这样当第一次到达终点就是最早到达的时候。机器的某一时刻的状态应该包括地点和朝向,此时用
visited[100][100][4]visited[100][100][4] visited[100][100][4]
多出的维度用来表示机器人所在的方向。

#include <cstdio>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>char map[200][200];         /* 读入迷宫所需数组 */
int visited[200][200][4];   /* 表示该位置是否被访问 */
int row,col;                /* 行,列 */
int sx[4]={-1,0,1,0};       /* 行走方向辅助数组 */
int sy[4]={0,1,0,-1};typedef struct Node{int x;int y;int face;int step;
}node;                      /* 机器人所走过的每个结点,记录坐标、朝向以及当前步数 */
node s,e;                   /* 起点终点 */
node queue[160000];         /* BFS所需要的队列 */int judge(int x,int y){     /* 判断是否可以到达 */return x>=0&&x<row&&y>=0&&y<col&&map[x][y]!='#';
}int BFS(){int i,j,k,top,tx,ty,ex,ey,face;ex=e.x,ey=e.y;queue[0]=s;             /* 将起点加入队列 */visited[s.x][s.y][0]=1;top=1;for(int i=0;i<top;i++){/* 开始搜索 *//* 向前进 */tx=queue[i].x+sx[queue[i].face];/* 使用辅助数组 */ty=queue[i].y+sy[queue[i].face];face=queue[i].face;if(judge(tx,ty)&&!visited[tx][ty][queue[i].face]){queue[top].x=tx;queue[top].y=ty;queue[top].face=queue[i].face;queue[top].step=queue[i].step+1;visited[tx][ty][face]=1;if(map[tx][ty]=='T'){   /* 判断是否目标 */return queue[top].step;}top++;}/* 向右转 */tx=queue[i].x;ty=queue[i].y;face=(queue[i].face+1)%4;if(!visited[tx][ty][face]){queue[top].x=tx;queue[top].y=ty;queue[top].face=face;queue[top].step=queue[i].step+1;visited[tx][ty][face]=1;if(map[tx][ty]=='T'){   /* 判断是否目标 */return queue[top].step;}top++;}/* 向左转 */tx=queue[i].x;ty=queue[i].y;face=(queue[i].face+3)%4;if(!visited[tx][ty][face]){queue[top].x=tx;queue[top].y=ty;queue[top].face=face;queue[top].step=queue[i].step+1;visited[tx][ty][face]=1;if(map[tx][ty]=='T'){   /* 判断是否目标 */return queue[top].step;}top++;}}return -1;                      /* 既无法到达目标 */
}
int main(){int n,i,j,k;char c;scanf("%d",&n);while(n--){scanf("%d %d",&row,&col);   /* 读入行列 */getchar();for(i=0;i<row;i++){for(j=0;j<col;j++){c=getchar();map[i][j]=c;if(map[i][j]=='S'){ /* 记录开始位置 */s.x=i;s.y=j;}else if(map[i][j]=='T'){/* 结束位置 */e.x=i;e.y=j;}}getchar();}s.face=0;s.step=0;memset(visited,0,sizeof(visited));printf("%d\n",BFS());}return 0;
}

2.2 Yet Another Multiple Problem

题目描述

给一个数n(1<n<10000)n(1<n<10000)n(1<n<10000),求nnn的最小的倍数xxx,使得xxx不包含mmm个特定的数字。输入的第一个数字表示nnn,第二个数表示mmm。接下来的一行有mmm个数字,表示不包含的数字。

输出为当前第几组数据以及最小倍数xxx,若xxx没有解,则输出-1。

输入样例

2345 3
7 8 9
100 1
0

输出样例

Case1: 2345
Case2: -1

完整程序

#include <cstring>
#include <cstdio>
#include <queue>
#include <string>
#include <algorithm>
#define N 10005
using namespace std;
bool vis[N],del[10];    /* 是否已经访问的记录数组,以及禁忌数字记录数组 */
int n,pre[N];           /* 记录前置数字来输出 */
char text[N];           /* 为输出准备的数组 */
bool bfs(){queue<int> q;q.push(0);int cur;while(!q.empty()){cur=q.front();q.pop();for(int i=0;i<10;i++){if(del[i]==1||cur==0&&i==0){/* 如果是不允许使用的数字或者当前余数和数字都为0,继续 */continue;}int yu=(cur*10+i)%n;    /* 计算新的余数 */if(vis[yu]){            /* 已经到达过,不保存 */continue;}text[yu]='0'+i;vis[yu]=true;pre[yu]=cur;            /* 记录到到达这个余数前面那个数 */q.push(yu);if(yu==0){return true;}}}return false;
}
void print(){string ans;int p=0;while(p!=0||ans.empty()){ans+=text[p];p=pre[p];}reverse(ans.begin(),ans.end());puts(ans.c_str());
}
int main(){int m,cas=0;while(scanf("%d%d",&n,&m)!=EOF){memset(vis,0,sizeof(vis));memset(del,0,sizeof(del));while(m--){int k;scanf("%d",&k);del[k]=1;}printf("Case%d: ",++cas);if(!bfs()){printf("-1\n");}else{print();}}return 0;
}

2.3 马的遍历

传送门

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
int n,m,x,y;
int ans[401][401];      /* 记录步数 */
bool visited[401][401]; /* 记录是否访问 */
const int dx[8]={-1,-2,-2,-1,1,2,2,1};
const int dy[8]={2,1,-1,-2,2,1,-1,-2};
queue<pair<int,int>> q; /* 不含有排序功能 */
int main(){memset(ans,-1,sizeof(ans));memset(visited,false,sizeof(visited));cin>>n>>m>>x>>y;ans[x][y]=0;visited[x][y]=true;q.push(make_pair(x,y));while(!q.empty()){int xx=q.front().first,yy=q.front().second;q.pop();for(int i=0;i<8;i++){int u=xx+dx[i],v=yy+dy[i];if(u<1||u>n||v<1||v>m||visited[u][v])continue;visited[u][v]=true;q.push(make_pair(u,v));ans[u][v]=ans[xx][yy]+1;}}for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){printf("%-5d",ans[i][j]);}printf("\n");}return 0;
}

2.4 Fire!

传送门

#include<bits/stdc++.h>
using namespace std;
struct Struct
{int X;//当前横坐标int Y;//当前纵坐标int Step;//当前时间
};
Struct Xy[1000001];
int Case;//数据组数
int N,M;//行、列
int Dx,Dy;//下一个坐标
bool GetAns;//记录是否找到解
bool Vis[1001][1001];//记录是否来过某点
char Map[1001][1001];//输入进来的地图
int Fire[1001][1001];//记录火焰到某个点所需要的时间
int Dir[4][2]={1,0,0,1,-1,0,0,-1};//每次往相邻的四个方向拓展
Struct First;//人物初始位置
Struct Nxt;//下一个点
Struct Now;//当前点
int main()
{int i,j;cin>>Case;while(Case--)//多组数据{queue<Struct>Q;//这就是队列cin>>N>>M;for(i=1;i<=N;i++){for(j=1;j<=M;j++){Fire[i][j]=N*M;//刚开始现将时间设的够大,方便取最小值Vis[i][j]=false;//刚开始每个点都没去过}}for(i=1;i<=N;i++){for(j=1;j<=M;j++){cin>>Map[i][j];//输入当前点的地形if(Map[i][j]=='F'){Nxt.X=i;Nxt.Y=j;Nxt.Step=0;Fire[i][j]=0;Q.push(Nxt);//如果是火源,加入队列}if(Map[i][j]=='#'){Fire[i][j]=0;//如果是墙壁,那么无法到达}if(Map[i][j]=='J'){First.X=i;//记录人物初始横坐标First.Y=j;//记录人物初始纵坐标First.Step=0;}}}while(!Q.empty())//第一个BFS{Now=Q.front();//取出队首Q.pop();//弹掉队首for(i=0;i<4;i++)//向四周扩散{Dx=Now.X+Dir[i][0];//下一个点的横坐标Dy=Now.Y+Dir[i][1];//下一个点的纵坐标if(Now.Step+1<Fire[Dx][Dy])//取较小值{Fire[Dx][Dy]=Now.Step+1;//更新较小值Nxt.X=Dx;Nxt.Y=Dy;Nxt.Step=Now.Step+1;Q.push(Nxt);//继续BFS}}}Q.push(First);//将人物初始位置加入队列GetAns=false;//先设为未找到答案while(!Q.empty())//第二个BFS{Now=Q.front();//取出队首Q.pop();//弹掉队首if(Now.X==1||Now.Y==1||Now.X==N||Now.Y==M)//如果到了边界,结束搜索{GetAns=true;//设为已经找到了解cout<<Now.Step+1<<endl;//输出步数,记得+1break;//结束搜索}for(i=0;i<4;i++)//向四周扩散{Dx=Now.X+Dir[i][0];//下一个点的横坐标Dy=Now.Y+Dir[i][1];//下一个点的纵坐标if(Now.Step+1<Fire[Dx][Dy]&&!Vis[Dx][Dy])//是否去过,并判断能否进去{Vis[Dx][Dy]=true;//标记为去过了Nxt.X=Dx;Nxt.Y=Dy;Nxt.Step=Now.Step+1;Q.push(Nxt);//加入队列}}}if(!GetAns)//如果没有找到答案,输出IMOPOSSIBLE{cout<<"IMPOSSIBLE"<<endl;}}return 0;
}

3. 深度优先搜索(Depth First Search,DFS)

概述

​ 深度优先搜索一般使用递归来完成,因此代码长度更短小。深度优先搜索遍历类似于树的先根遍历,是树的先根遍历的推广。假设初始状态是图中所有顶点未曾被访问,则深度优先搜索可从图中某个顶点vvv出发,访问此顶点,然后依次从vvv的未被访问的邻接点出发深度优先遍历此图,则另选图中一个未曾被访问的顶点作为起始点,重复上述过程,直至图中所有顶点都被访问到为止。

​ 深度优先搜索是一个递归的过程。为了判断在遍历过程中每个点是否被访问过,需要设定visited[maxn]visited[maxn]visited[maxn]数组来记录该顶点是否被访问。

伪代码

void DFS(node v){/* 从第v个顶点出发递归地深度优先遍历 */visited[v]=true;if(!visited[w]) DFS(w);     /* 对v的尚未被访问的邻接顶点w递归调用DFS */
}

3.1 八皇后问题

传送门

图示遍历过程

#include <iostream>
using namespace std;
const int N = 1e3;
int n;
int a[100],b[100],c[100],d[100];
/* a数组表示的是行 */
/* b数组表示的是列 */
/* c数组表示的是左下到右上的对角线 */
/* d数组表示的是左上到右下的对角线 */
int total;  /* 记录解的总个数 */
void print1(){if(total<=2){for(int k=1;k<=n;k++)cout<<a[k]<<" ";cout<<endl;}total++;
}
void dfs(int i){if(i>n){print1();return;}else{for(int j=1;j<=n;j++){if((!b[j])&&(!c[i+j])&&(!d[i-j+n])) /* 如果没有皇后占领 */{a[i]=j;     /* 打标记 */b[j]=1;c[i+j]=1;d[i-j+n]=1;dfs(i+1);   /* 继续向下搜索 */b[j]=0;     /* 如果行不通则清楚标记 */c[i+j]=0;d[i-j+n]=0;}}}
}
int main(){cin>>n;dfs(1);cout<<total;return 0;
}

3.2 迷宫

传送门

AC代码

#include <cstdio>
int n,m,t;
int sx,sy,ex,ey,l,r;
int ma[6][6];
int visited[6][6];
int dx[4]={0,0,1,-1};
int dy[4]={-1,1,0,0};
int ans=0;
void dfs(int x,int y){if(x==ex&&y==ey){ans++;return;}else{for(int i=0;i<=3;i++){if(x+dx[i]>=1&&x+dx[i]<=n&&y+dy[i]>=1&&y+dy[i]<=m&&visited[x+dx[i]][y+dy[i]]==0&&ma[x+dx[i]][y+dy[i]]==1){visited[x][y]=1;dfs(x+dx[i],y+dy[i]);visited[x][y]=0;}}}
}
int main(){scanf("%d %d %d",&n,&m,&t);for(int ix=1;ix<=n;ix++)for(int iy=1;iy<=m;iy++)ma[ix][iy]=1;scanf("%d %d %d %d",&sx,&sy,&ex,&ey);for(int i=1;i<=t;i++){scanf("%d %d",&l,&r);ma[l][r]=0;}dfs(sx,sy);printf("%d",ans);return 0;
}

3.3 奇怪的电梯

传送门

dfs做法

#include <cstdio>
#include <algorithm>
using namespace std;
int n,a,b,ans=0x7ffffff;
int reach[205];
bool visited[205];
void dfs(int now,int sum){if(now==b)ans=min(ans,sum);if(sum>ans) return;visited[now]=1;if(now+reach[now]<=n&&!visited[now+reach[now]]) dfs(now+reach[now],sum+1);if(now-reach[now]>=1&&!visited[now-reach[now]]) dfs(now-reach[now],sum+1);visited[now]=0; /* 清除标记 */
}
int main(){scanf("%d %d %d",&n,&a,&b);for(int i=1;i<=n;i++) scanf("%d",&reach[i]);visited[a]=1;dfs(a,0);if(ans!=0x7ffffff) printf("%d",ans);else printf("-1");return 0;
}

bfs做法

#include<cstdio>
#include <queue>
using namespace std;
int n,a,b,to[205];
bool vis[205];
struct node{int id,step;}x;
queue<node> q;
int main()
{scanf("%d%d%d",&n,&a,&b);for(int i=1;i<=n;i++) scanf("%d",&to[i]);q.push((node){a,0});while(q.size()){x=q.front();q.pop();if(x.id==b) break;if(x.id+to[x.id]<=n&&!vis[x.id+to[x.id]]){q.push((node){x.id+to[x.id],x.step+1});vis[x.id+to[x.id]]=1;}if(x.id-to[x.id]>=1&&!vis[x.id-to[x.id]]){q.push((node){x.id-to[x.id],x.step+1});vis[x.id-to[x.id]]=1;}}if(x.id==b) printf("%d",x.step);else printf("-1");return 0;
}

3.4 [NOIP2017 提高组] 奶酪

传送门

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int MAX_N=1010;
struct cir{double x,y,z;bool operator<(const cir &cpr)const{return z<cpr.z;}
}p[MAX_N];
bool fnd=0;
int n;
double h,r;
bool visited[MAX_N];
double dist(double x1,double y1,double z1,double x2,double y2,double z2){return sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)+(z2-z1)*(z2-z1));
}
void dfs(cir now,int num){if(now.z+r>=h){fnd=1;return;}visited[num]=1;for(int i=1;i<=n;i++){if(fnd)return;if(!visited[i]&&dist(now.x,now.y,now.z,p[i].x,p[i].y,p[i].z)<=r*2)dfs(p[i],i);}
}
int main(){int t;scanf("%d",&t);while(t--){memset(visited,0,sizeof(visited));memset(p,0,sizeof(p));fnd=0;scanf("%d%lf%lf",&n,&h,&r);for(int i=1;i<=n;i++)scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].z);sort(p+1,p+n+1);for(int i=1;i<=n;i++)if(p[i].z-r<=0)dfs(p[i],i);if(fnd)printf("Yes\n");elseprintf("No\n");}return 0;
}

3.5 [USACO05DEC]Scales S

传送门

#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
ll sum[1005],a[1005],ans,n,c;
inline ll read(){char c=getchar();int f=1;ll x=0;while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^'0');c=getchar();}return x*f;
}
void dfs(int now,long long x)
{if(x>c)return;if(sum[now-1]+x<=c){ans=max(ans,sum[now-1]+x);return;}ans=max(ans,x);for(int i=1;i<now;i++)dfs(i,x+a[i]);return;
}
int main()
{n=read();c=read();for(int i=1;i<=n;i++){a[i]=read();sum[i]=sum[i-1]+a[i];   /* 前缀和 */}dfs(n+1,0);printf("%lld\n",ans);return 0;
}

3.6 油田(连通块)

传送门

4. 剪枝

4.1 小木棍 [数据加强版]

传送门

#include<bits/stdc++.h>
using namespace std;
inline int read(){int x=0; bool f=1; char c=getchar();for(;!isdigit(c);c=getchar()) if(c=='-') f=0;for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';if(f) return x;return 0-x;
}
int n,m,a[66],next[66],cnt,sum,len;
bool used[66],ok; //used数组即优化5的vis数组,记录每根木棍是否用过;ok记录是否已找到答案。
bool cmp(int a,int b){return a>b;}
void dfs(int k,int last,int rest){ //k为正在拼的木棍的编号,last为正在拼的木棍的前一节编号,rest为该木棍还未拼的长度int i;if(!rest){ //未拼的长度为0,说明这根原始长棍拼完了,准备拼下一个 if(k==m){ok=1; return;} //优化6,全部拼完并符合要求,找到答案,直接返回 for(i=1;i<=cnt;i++) //找一个还没用的最长的木棍打头即可。反正要想全都拼接成功,每根木棍都得用上 if(!used[i]) break;used[i]=1; dfs(k+1,i,len-a[i]);used[i]=0;if(ok) return; //优化6,找到答案就退出 }//优化4,二分找第一个 木棍长度不大于未拼长度rest 的位置 int l=last+1, r=cnt, mid;while(l<r){mid=(l+r)>>1;if(a[mid]<=rest) r=mid;else l=mid+1;}for(i=l;i<=cnt;i++){if(!used[i]){ //优化5,判断木棍是否用过 used[i]=1;dfs(k,i,rest-a[i]);used[i]=0;if(ok) return; //优化6,找到答案就退出 if(rest==a[i] || rest==len) return; //优化7 i=next[i]; //优化3 if(i==cnt) return;}}//到了这里,说明这时候拼不成当前这根原始木棍了,传回失败信息并修改之前拼的木棍
}
int main(){n=read();int d;for(int i=1;i<=n;i++){d=read();if(d>50) continue;a[++cnt]=d;sum+=d;}sort(a+1,a+cnt+1,cmp); //优化1,木棍按长度从大到小排序 //优化3,预处理next数组 next[cnt]=cnt;for(int i=cnt-1;i>0;i--){if(a[i]==a[i+1]) next[i]=next[i+1];else next[i]=i;}for(len=a[1];len<=sum/2;len++){ //枚举原始长度 if(sum%len!=0) continue; //如果不能拼出整数根 就跳过 m=sum/len; //优化6中的那个计算 ok=0;used[1]=1;dfs(1,1,len-a[1]);used[1]=0;if(ok){printf("%d\n",len); return 0;} //优化6,输出答案,退 }printf("%d\n",sum); return 0;
}

4.2 [NOIP2004 提高组] 虫食算(数论+搜索)

传送门

#include<cstdio>
#include<algorithm>
using namespace std;
#define maxn 30
int a[maxn],b[maxn],c[maxn];
int num[maxn],Next[maxn],n,cnt;
char s1[maxn],s2[maxn],s3[maxn];
bool used[maxn];
bool Judge() {for(int i=n-1,x=0;i>=0;i--) {int A=num[a[i]],B=num[b[i]],C=num[c[i]];if((A+B+x)%n!=C) return false;x=(A+B+x)/n;}return true;
}
bool CanPrune() {//prune: 剪枝—百度翻译。if(num[a[0]]+num[b[0]]>=n)return true;for(int i=n-1;i>=0;i--) {int A=num[a[i]],B=num[b[i]],C=num[c[i]];if(A==-1||B==-1||C==-1) continue;if((A+B)%n!=C&&(A+B+1)%n!=C)return true;}return false;
}
void Print() {for(int i=0;i<n;i++)printf("%d ",num[i]);exit(0);
}
void dfs(int x) {if(CanPrune()==true) return;if(x==n) {if(Judge()==true) Print();return;}for(int i=n-1;i>=0;i--)if(used[i]==false) {num[Next[x]]=i;used[i]=true;dfs(x+1);num[Next[x]]=-1;used[i]=false;}return;
}
inline int id(char c) {return c-'A';
}
void GetNext(int x) {if(used[x]==false) {used[x]=true;Next[cnt++]=x;}return;
}
int main() {scanf("%d",&n);scanf("%s%s%s",s1,s2,s3);for(int i=0;i<n;i++) {a[i]=id(s1[i]);b[i]=id(s2[i]);c[i]=id(s3[i]);num[i]=-1;}for(int i=n-1;i>=0;i--) {GetNext(a[i]);GetNext(b[i]);GetNext(c[i]);}for(int i=0;i<n;i++) used[i]=false;dfs(0);return 0;
}

5. A*算法

5.1 八数码难题(哈希+搜索)

传送门

6. 迭代加深搜索

6.1 快速幂计算 Power Calculus

传送门

7. 双向宽度优先搜索

7.1 八数码难题

传送门

8. 舞蹈链

搜索算法(DFS,BFS等)相关推荐

  1. SDNUOJ 1610.L 解救小M|搜索算法DFS/BFS及其实现

    目录 题面 题目分析 解题方法 方法1 AC代码 方法2 AC代码 题面 Description 小 M 现在被困在了一个山洞里需要你去解救. 格式化的说,可以把距离看成一个数轴,你现在在位置 1,小 ...

  2. 分享两个常见的搜索算法:BFS和DFS

    本文分享自华为云社区<BFS和DFS算法初探>,作者: ayin. 本次分享两个常见的搜索算法 1.BFS 即广度优先搜索 2.DFS 即深度优先搜索 岛屿数量 给定一个由 '1'(陆地) ...

  3. 1.5万字详述 | 全开源:python写小游戏+AI强化学习与传统DFS/BFS控制分别实现

    简介:本周的强化学习我们来到实践部分.我以我在 GitHub 上开源的项目 PiperLiu / Amazing-Brick-DFS-and-DRL 为对象,从零开始与各位朋友分享:如何用 pytho ...

  4. python 拓扑排序 dfs bfs_图遍历算法之DFS/BFS

    在计算机科学, 图遍历(Tree Traversal,也称图搜索)是一系列图搜索的算法, 是单次访问树结构类型数据(tree data structure)中每个节点以便检查或更新的一系列机制.图遍历 ...

  5. DFS BFS简单理解

    文章目录 BFS DFS介绍 实现思路 DFS BFS怎么应用 DFS BFS对比 又水了一篇博客呜呜,第一次尝试写DFS和BFS,做题也迷迷糊糊,看着大佬文章简单写了写总结,后续会补上DFS和BFS ...

  6. (3)【全局路径规划】图搜索的路径探索方法--DFS\BFS\DFS-ID、贪心算法、Dijkstra和A*、JPS、.hybird A*、

    系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录 系列文章目录 前言 图搜索的方法 0.基础知识介绍 1)图的概念 2)规划配置空间(C- ...

  7. 【DFS/BFS】NYOJ-58-最少步数(迷宫最短路径问题)

    [题目链接:NYOJ-58] 经典的搜索问题,想必这题用广搜的会比较多,所以我首先使的也是广搜,但其实深搜同样也是可以的. 不考虑剪枝的话,两种方法实践消耗相同,但是深搜相比广搜内存低一点. 我想,因 ...

  8. POJ2308连连看dfs+bfs+优化

    DFS+BFS+MAP+剪枝 题意:       就是给你一个10*10的连连看状态,然后问你最后能不能全部消没? 思路:      首先要明确这是一个搜索题目,还有就是关键的一点就是连连看这个游戏是 ...

  9. 链式前向星模板 建图+dfs+bfs+dijkstra

    边没有用struct封装起来,节点和边的计数起点如果不符合习惯可以稍作修改 建图+DFS+BFS #include <cstdio> #include <cstring> #i ...

  10. 算法 - DFS/BFS

    DFS函数大概率会传递"位置信息",根据位置信息获取下一步的选择,(大部分是在循环中)选择.执行.回退 例如N皇后的 棋盘位置(x, y),组合问题的 idx DFS函数的目的是, ...

最新文章

  1. 2013年,我跟哥们都是大厂Java工程师!后来,他转行了!现在,他的收入是我的5倍!...
  2. java 转时区_java – 时区转换
  3. 伦巴时间步的动作要领_军训动作要领已到,请签收
  4. 注册表操作命令reg
  5. 深度学习入门:10门免费线上课程推荐
  6. mysql怎么从1开始递增
  7. 【转】计算机键盘功能键作用
  8. python+selenium 使用for循环,遍历 定位 获取 单个元素中想要的值
  9. 韩顺平 零基础30天学会Java 学习笔记
  10. 矩阵的转置与求导运算
  11. 译:谷歌OKR指导手册(全文)
  12. java-eclipse-tomcat配置运行发布网站
  13. nuxt.js框架使用vue-waterfall-easy插件如何引入--语法引用
  14. sourceinsight 查看源码的利器
  15. 重新理解创业:一个创业者的中途思考
  16. DTV下的AD Switch和Hearing Impaired功能介绍
  17. 计算机网络传输层简介
  18. 12-11 网易实习一面
  19. Java 性能调优 概念详解 小白教程
  20. 2019年天猫年货节淘客文案(全网节日淘客文案通用最新版持续更新)

热门文章

  1. C# 操作mysql-创建表、插入更新数据
  2. 用自定义IHttpModule实现URL重写 1
  3. Java开发面试题汇总 -- 精选版(附答案)
  4. 如何在excel中打钩
  5. 解决cef加载flash时弹出黑框的问题
  6. Android ImageView 正确使用姿势
  7. 【转】搞清楚脚本中这些函数的调用规律
  8. Screen Orientation for Windows Phone
  9. 使用php glob函数查找文件,遍历文件目录(转)
  10. mod_expires和mod_deflate的配置