DAY4 搜索


搜索类型

今天的主要内容为深度优先搜索(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
} 

结束

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值