搜索类型
今天的主要内容为深度优先搜索(DFS)和广度优先搜索(BFS)
1.广度优先搜索(BFS)
介绍
BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。类似树的按层遍历
广度优先搜索的逻辑就是一层一层的搜索。以一个节点为初始节点,每一轮搜索与上一层相隔距离为1个单位的节点。
例子
Question
Y和M要在距离他们最近的@集合,求出他们到他们距离和最近的@
The input contains multiple test cases.
Each test case include, first two integers n, m. (2<=n,m<=200).
Next n lines, each line included m character.
‘Y’
‘M’
‘#’ forbid road;
‘.’ Road.
‘@’
Sample Input
4 4
Y.#@
…
.#…
@…M
4 4
Y.#@
…
.#…
@#.M
5 5
Y…@.
.#…
.#…
@…M.
#…#
Sample Output
66
88
66
例子代码
#include<bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
//(x,y) pii Point=make_pair(x,y)
//获取x x=Point.first;
//获取y y=Point.second;
const int N=200+10;
const int M=10;
const int inf=0x3f3f3f3f; //无穷大
char s[N][N];
int sx[M],sy[M];
int n,m,dis[M][N][N];
int dx[M]={0,0,1,-1};
int dy[M]={1,-1,0,0};
void bfs(int id){
queue<pii>q; //先进先出
q.push(make_pair(sx[id],sy[id]));
dis[id][sx[id]][sy[id]]=0;
while(!q.empty()){
int x=q.front().first,y=q.front().second;
q.pop(); //记得
for(int i=0;i<4;i++){
int xx=x+dx[i],yy=y+dy[i]; //下一步要走的点
if(1<=xx && xx<= n && 1<=yy && yy<=m && dis[id][xx][yy]==inf && s[xx][yy]!='#'){
dis[id][xx][yy]=dis[id][x][y]+1;
q.push(make_pair(xx,yy));
}
}
}
}
int main(){
while(scanf("%d %d",&n,&m)!=EOF){
memset(dis,inf,sizeof dis);
for(int i=1;i<=n;i++)scanf("%s",s[i]+1);
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){
if(s[i][j]=='Y')sx[0]=i,sy[0]=j;
if(s[i][j]=='M')sx[1]=i,sy[1]=j;
}
bfs(0),bfs(1);
int res=inf;
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)if(s[i][j]=='@'){
res=min(res,dis[0][i][j]+dis[1][i][j]);
}
printf("%d\n",res*11);
}
}
2.深度优先搜索(DFS)
介绍
深度优先搜索所遵循的搜索策略是尽可能“深”地搜索树。它的基本思想是:为了求得问题的解,先选择某一种可能情况向前(子节点)探索,在探索过程中,一旦发现原来的选择不符合要求,就回溯至父亲节点重新选择另一节点,继续向前探索,如此反复进行,直至求得最优解。深度优先搜索的实现方式可以采用递归或者栈来实现。
就是每次搜索都持续到不能继续搜索,然后再回溯到上一个可以继续深度搜索的父亲节点一直持续
这种方法的需要遍历所有的情况所以时间复杂度较高,需要我们运用方法如 预处理、对情况进行缩减
例子
Question
N皇后问题
遇到0时退出
Sample Input
1
8
5
0
Sample Output
1
92
10
例子代码
#include<bits/stdc++.h>
using namespace std;
const int N =10+10;
int f[N],vis[N],n,cnt; //vis[i] 代表第i列之前已经被第vis[i]行放置
bool check(int row,int col){ //判断第row行第col列能不能放置棋子
if(vis[col])return false;
for(int i=1;i<=n;i++){ //45°
if(vis[i] && abs(i-col)==abs(vis[i]-row))return false;
}
return true;
}
void dfs(int now){ //当前准备放置第now行
if(now==n+1){ //是否对答案有贡献
cnt++;
return;
}
for(int i=1;i<=n;i++){
if(check(now,i)){
vis[i]=now; //第i列已经由第now行放置
dfs(now+1); //放置下一行
vis[i]=0; //还原
}
}
}
int main(){
for(n=1;n<=10;n++){
memset(vis,0,sizeof vis);
cnt=0;
dfs(1);
f[n]=cnt;
}
int x;while(scanf("%d",&x)!=EOF){
if(x==0)break;
printf("%d\n",f[x]);
}
}
辅助方法
1.建边方法
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N=100000+10;
const int M=100+10;
vector<int>G[N];
//x->y
void way1(){
G[x].push_back(y); //加边方式
int sz=G[x].size(); //遍历方式
for(int i=0;i<sz;i++){
int to=G[x][i];
}
}
//链式前向星
int tot,ver[N<<1],Next[N<<1],head[N];
//tot标号,ver[]存储每个编号的边连出去的点,Next[]往前连接的边的编号,head[]最后一个编号的边
void add(int u,int v){ //添加一条从u->v的边
++tot;ver[tot]=v;
Next[tot]=head[u];head[u]=tot;
}
void way2(){
add(x,y); //加边方式
for(int i=head[x];i;i=Next[i]){ //遍历方式
int to=ver[i];
}
}
int main(){
memset(head,0,sizeof head); // 使用这种方法前需要清空head
}
2.二分图染色
案例
Question
Young theoretical computer scientist wyh2000 is teaching his pupils.
Wyh2000 has n pupils.Id of them are from 1 to n.In order to increase the cohesion between pupils,wyh2000 decide to divide them into 2 groups.Each group has at least 1 pupil.
Now that some pupils don’t know each other(if a doesn’t know b,then b doesn’t know a).Wyh2000 hopes that if two pupils are in the same group,then they know each other,and the pupils of the first group must be as much as possible.
Please help wyh2000 determine the pupils of first group and second group. If there is no solution, print “Poor wyh”.
Input
In the first line, there is an integer T indicates the number of test cases.
For each case, the first line contains two integers n,m indicate the number of pupil and the number of pupils don’t konw each other.
In the next m lines,each line contains 2 intergers x,y(x<y),indicates that x don’t know y and y don’t know x,the pair (x,y) will only appear once.
T≤10,0≤n,m≤100000
Sample Input
2
8 5
3 4
5 6
1 2
5 8
3 5
5 4
2 3
4 5
3 4
2 4
Sample Output
5 3
Poor wyh
案例代码
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pii pair<int,int>
const int N=100000+10;
const int M=10+10;
const int inf=0x3f3f3f3f;
const LL INF=2e18;
int head[N],ver[N<<1],Next[N<<1],tot;
int n,m,col[N],ans[M];
bool ok=false;
void add(int u,int v){
ver[++tot]=v;Next[tot]=head[u];head[u]=tot;
}
void dfs(int u,int c){
if(ok)return;
col[u]=c;
ans[c]++;
for(int i=head[u];i;i=Next[i]){
int y=ver[i];
if(col[y]!=-1){
if(col[y]==c){
ok=true;
return;
}
}
else dfs(y,c^1);
}
}
void gan(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)head[i]=0;tot=0;
for(int i=1;i<=n;i++)col[i]=-1;
for(int i=1;i<=m;i++){
int x,y;scanf("%d %d",&x,&y);
add(x,y),add(y,x);
}
if(n<=1){
puts("Poor wyh");
return;
}
int res1=0,res2=0;
for(int i=1;i<=n;i++){
if(col[i]==-1){
ans[0]=ans[1]=0;ok=false;
dfs(i,0);
if(ok){
puts("Poor wyh");
return;
}
res1+=max(ans[0],ans[1]);
res2+=min(ans[0],ans[1]);
}
}
if(m==0)res1--,res2++;
printf("%d %d\n",res1,res2);
}
int main(){
#ifdef _DEBUG
freopen("out.txt", "r", stdin);
int tt = clock();
#endif
ios_base::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cout << fixed << setprecision(15);
int _T=1;scanf("%d",&_T);
while(_T--){
gan();
}
#ifdef _DEBUG
cerr << "TIME = " << clock() - tt << endl;
tt = clock();
#endif
}
3.拓扑排序
对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。
执行步骤
拓扑排序算法主要是循环执行以下两步,直到不存在入度为0的顶点为止。
(1) 选择一个入度为0的顶点并输出之;
(2) 从网中删除此顶点及所有出边。
循环结束后,若输出的顶点数小于网中的顶点数,则输出“有回路”信息,否则输出的顶点序列就是一种拓扑序列。
取自百度百科
案例
Question
ACM-DIY is a large QQ group where many excellent acmers get together. It is so harmonious that just like a big family. Every day,many “holy cows” like HH, hh, AC, ZT, lcc, BF, Qinz and so on chat on-line to exchange their ideas. When someone has questions, many warm-hearted cows like Lost will come to help. Then the one being helped will call Lost “master”, and Lost will have a nice “prentice”. By and by, there are many pairs of “master and prentice”. But then problem occurs: there are too many masters and too many prentices, how can we know whether it is legal or not?
We all know a master can have many prentices and a prentice may have a lot of masters too, it’s legal. Nevertheless,some cows are not so honest, they hold illegal relationship. Take HH and 3xian for instant, HH is 3xian’s master and, at the same time, 3xian is HH’s master,which is quite illegal! To avoid this,please help us to judge whether their relationship is legal or not.
Please note that the “master and prentice” relation is transitive. It means that if A is B’s master ans B is C’s master, then A is C’s master.
Input
The input consists of several test cases. For each case, the first line contains two integers, N (members to be tested) and M (relationships to be tested)(2 <= N, M <= 100). Then M lines follow, each contains a pair of (x, y) which means x is y’s master and y is x’s prentice. The input is terminated by N = 0.
TO MAKE IT SIMPLE, we give every one a number (0, 1, 2,…, N-1). We use their numbers instead of their names.
Output
For each test case, print in one line the judgement of the messy relationship.
If it is legal, output “YES”, otherwise “NO”.
Sample Input
3 2
0 1
1 2
2 2
0 1
1 0
0 0
Sample Output
YES
NO
案例代码
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pii pair<int,int>
const int N=100+10;
const int M=100000+10;
const int inf=0x3f3f3f3f;
const LL INF=2e18;
int n,m,deg[N];
vector<int>G[N];
bool topo(){
queue<int>q;
for(int i=0;i<n;i++)if(!deg[i])q.push(i);
while(!q.empty()){
int x=q.front();
q.pop();int sz=G[x].size();
for(int i=0;i<sz;i++){
int y=G[x][i];
if(--deg[y]==0)q.push(y);
}
}
for(int i=0;i<n;i++)if(deg[i])return false;
return true;
}
void init(){
for(int i=0;i<n;i++)G[i].clear(),deg[i]=0;
}
void gan(){
while(scanf("%d %d",&n,&m)!=EOF){
if(n==0)break;
init();
for(int i=1;i<=m;i++){
int x,y;scanf("%d %d",&x,&y);
G[x].push_back(y);deg[y]++;
}
if(topo())puts("YES");
else puts("NO");
}
}
int main(){
#ifdef _DEBUG
freopen("out.txt", "r", stdin);
int tt = clock();
#endif
ios_base::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cout << fixed << setprecision(15);
int _T=1;
while(_T--){
gan();
}
#ifdef _DEBUG
cerr << "TIME = " << clock() - tt << endl;
tt = clock();
#endif
}