DFS刷题总结(含PAT、LeetCode部分题目)

DFS

BFS(过几天会出)

算法思想:

访问图的某一起始点v,从v开始出发,访问其任一邻接顶点w1,然后从w1开始,访问与w1邻接但未被访问过的点w2,然后从w2出发,进行类似的操作,直到到达所有邻接点都被访问过的点u为止。然后需要回退,退回前一次访问过的顶点,看看是否还没有访问过得到邻接点。如果有就访问,重复上述类似操作,没有就继续回退,重复上述操作。直到图中所有点都被访问位置 。

伪代码:

void DFS(Graph G, int v){	//图G为邻接矩阵类型
	cout << v;				//访问第v个顶点
	visited[v] = true;		//做标志
	for( w = 0; w < G.vexnum; w++)	//依次检查邻接矩阵v所在的行
	{
 		if(G.arcs[v][w] != 0 && ! visited[w])	//如果w是v的邻接点,且w未被访问,则递归调用DFS
 			DFS(G,w);
 	}
}

重点:

深度优先遍历这种算法搜索到一个新节点是,需要对其邻接结点进行遍历,遍历的顺序类似于栈的先入后出,由于栈和递归在某种意义上执行顺序可以等价,所以我们一般使用递归来实现解决该类问题。

例题:

ps:会给出每一题的代码以及细致的注释,并总结出一套刷题模板

一.入门级

LeetCode:岛屿最大面积

分析题目信息:①输入的数据结构是一个矩阵。②该题的邻接节点即一个点的上下左右四周。

③求最大岛屿面积,可理解为从某个1开始遍历四周得到的最多1的集合。

题解如下

class Solution {
public:
//深度优先遍历一般在遍历图的时候,一个点的邻接结点是其相邻连接的点
//而这种题型一般邻接结点时上{-1,0} 右{0,1} 下{1,0} 左{0,-1}
//所以direction只是一个方便遍历其四周邻接节点的一种处理方案,了解意思即可
    vector<int> direction{-1,0,1,0,-1};
    int maxAreaOfIsland(vector<vector<int>>& grid) {
        //1.判空,根据题目要求而定
        if(grid.empty()||grid[0].empty()) return 0;
        int max_area=0;
        int m=grid.size(),n=grid[0].size();
        //2.用于记录点是否被访问过,比较固定的写法,其实判重的方法不少,但是这种比较容易理解
        vector<vector<bool>> visit(m,vector<bool>(n,false));
        //3.该题结构是矩阵,正常的循环遍历即可
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                //只有1才是岛屿
                if(grid[i][j]==1){
                    //从这个岛屿开始深度优先遍历
                    max_area=max(max_area,dfs(grid,i,j,visit));
                }
            }
        }
        return max_area;
    }
    //深度优先搜索
    int dfs(vector<vector<int>>& grid,int l,int r,vector<vector<bool>>& visit){
        //条件判断,必须是1的陆地,不然直接返回
        if(grid[l][r]==0) return 0;
        int area=1,x,y;
        //记录为访问过
        visit[l][r]=true;
        //遍历邻接节点
        for(int i=0;i<4;i++){
            x=l+direction[i];
            y=r+direction[i+1];
            //需要判断是否越界以及是否被访问过,未越界且未被访问过才行
            if(x>=0&&x<grid.size()&&y>=0&&y<grid[0].size()&&!visit[x][y]){
                //往邻接节点深度遍历,即递归调用
                area+=dfs(grid,x,y,visit);
            }
        }
        return area;
    }
};

LeetCode:图像渲染

分析题目信息:①输入的数据结构是一个矩阵。②题目给定开始渲染的位置,则表明指定了开始深度遍历的起点。③按要求重新上色,即从起点开始深度优先遍历,将符合条件的节点重新上色即可。

题解如下

class Solution {
public:
//经典遍历邻接结点工具direction
    vector<int> direction{-1, 0, 1, 0, -1};
    vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int newColor) {
        int m=image.size(),n=image[0].size();
        //经典记录是否的工具visit数组
        vector<vector<bool>> visit(m,vector<bool>(n,false));
        int tag=image[sr][sc];
        //从题目给定的重新上色点开始深度优先遍历
        dfs(image,sr,sc,newColor,visit,tag);
        return image;
    }

    void dfs(vector<vector<int>>& image,int sr
    ,int sc,int nc,vector<vector<bool>>& visit,int tag){
        //经典条件判断,像素符合给该要求的才更改,否则返回
        if(image[sr][sc]!=tag) return;
        //更改像素
        image[sr][sc]=nc;
        //记录为访问过
        visit[sr][sc]=true;
        int x,y;
        //遍历邻接结点
        for(int i=0;i<4;i++){
            x=sr+direction[i];
            y=sc+direction[i+1];
            需要判断是否越界以及是否被访问过,未越界且未被访问过才行
            if(x>=0&&x<image.size()&&y>=0&&y<image[0].size()&&!visit[x][y]){
                //往邻接结点深度遍历,即递归调用
                dfs(image,x,y,nc,visit,tag);
            }
        }
    }
};

刷题模板总结:

int/void example(Collection c){
    //1.对集合c进行判空,根据题目要求而定
    if(c.empty()) return;
    //2.根据遍历的数据结构定义一个相同记录访问的数组
    visit;
    //3.遍历数据结构,关键是找到深度优先遍历的起点
    for{
        dfs(param)
    }
}

int/void dfs(param){
    //1.一般递归函数都有个返回null或者直接返回的条件判断,这也不例外
    //视题目而定
    if return;
    //2.因题而异,是题目逻辑的核心操作,比如计数+1,比如此时遍历的节点值进行修改等操作
    service
    //3.基本固定,记录为访问过
    visit=true;
    //4.遍历邻接节点
    //其中如果是遍历四周的类型,建议使用direction{-1,0,1,0,-1},比较方便
    for
    //5.对于给邻接节点进行条件判断,一般是越界和访问记录的判定
        if(未越界&&未被访问过)
    //6.最后符合条件的邻接节点递归调用即可
            dfs()
}

2.拔高级

PAT甲级A1090 Highest Price in Supply Chain

题目描述

A supply chain is a network of retailers(零售商), distributors(经销商), and suppliers(供应商)-- everyone involved in moving a product from supplier to customer.Starting from one root supplier, everyone on the chain buys products from one's supplier in a price P and sell or distribute them in a price that is r% higher than P.  It is assumed that each member in the supply chain has exactly one supplier except the root supplier, and there is no supply cycle.Now given a supply chain, you are supposed to tell the highest price we can expect from some retailers.

输入描述

Each input file contains one test case.  For each case, The first line contains three positive numbers: N (<=105), the total number of the members in the supply chain (and hence they are numbered from 0 to N-1); P, the price given by the root supplier; and r, the percentage rate of price increment for each distributor or retailer.  Then the next line contains N numbers, each number Si is the index of the supplier for the i-th member.  Sroot for the root supplier is defined to be -1.  All the numbers in a line are separated by a space.

输出描述

For each test case, print in one line the highest price we can expect from some retailers, accurate up to 2 decimal places, and the number of retailers that sell at the highest price.  There must be one space between the two numbers.  It is guaranteed that the price will not exceed 1010.

输入例子

9 1.80 1.00
1 5 4 4 -1 4 5 3 6

输出例子

1.85 2

题意解读:输入第一行有三个数,分别是供应链成员总数量、根供应商卖出的价格、每个分销商或零售商的价格增长率。

第二行各供应链成员的的供应商情况,可以理解为父节点。也就是说第一位成员的父节点是索引为1的成员(也就是第二位成员),其他同理。假设数组为s,也就是说第i+1位(索引是i,因为数组是从0开始,而一般我们习惯从第一位开始)的父节点是第s[i]+1位成员。

由于本题就是说每次父节点卖给子节点加个都要增加一个百分比,求最大价格,可以等价于求树的最大深度,只是多了一个求处于最大深度的节点的个数。

我们可以画出给的例子的数据结构

其中节点数字就是成员的索引。很明显是个树结构,所以 

#include<iostream>
#include<vector>
#include<iomanip>
using namespace std;

void dfs(int node,double P,double r,int &count,double &max,vector<vector<int> > &tree){
	if(tree[node].size()){ //有下一层,即代表有子节点
        //价格按倍率增长 
		P*=r;
	} 
    //如果大于最大值,就更新最大值,由于更新了最大值,那么最大值节点总数就初始化为1
	else if(P>max){
		max=P;
		count=1;//跟新深度后,结点数重新开始计算 
	} 
    //如果相等则最大值+1
	else if(P==max){ //相等则节点数+1 
		count++;
	} 
    //遍历其邻接结点,递归调用
	for(int i=0;i<tree[node].size();i++){
		dfs(tree[node][i],P,r,count,max,tree);
	}
} 

int main(){
	int  N,count=0,root;
	double r,P,max=0;
    //获取第一行输入
	cin>>N>>P>>r;
	r=r/100+1;
	vector<vector<int> > tree(N);
	
    //获取第二行输入
	int a[N];
	for(int i=0;i<N;i++) {
		cin>>a[i];
	} 
	//根据输入建图 
	for(int i=0;i<N;i++) {
		if(a[i]==-1){
			root=i;//确定root得到索引 
		} 
		else
		{
			tree[a[i]].push_back(i);//构造图结构,通过索引关系来构造
			//类似于二维数组,父亲索引(对应自己的值)和自己的索引(对应儿子的值)
		} 
	}
	
	//dfs遍历,从根节点开始深度优先遍历 
	dfs(root,P,r,count,max,tree);
    //得到结果保留两位小数
	cout<<fixed<<setprecision(2)<<max<<" "<<count;
	
} 

PAT甲级A1094 The Largest Generation

题目描述

A family hierarchy is usually presented by a pedigree tree where all the nodes on the same level belong to the same generation.  Your task is to find the generation with the largest population.

输入描述

Each input file contains one test case.  Each case starts with two positive integers N (<100) which is the total number of family members in the tree (and hence assume that all the members are numbered from 01 to N), and M (<N) which is the number of family members who have children.  Then M lines follow, each contains the information of a family member in the following format:ID K ID[1] ID[2] ... ID[K]where ID is a two-digit number representing a family member, K (>0) is the number of his/her children, followed by a sequence of two-digit ID's of his/her children. For the sake of simplicity, let us fix the root ID to be 01.  All the numbers in a line are separated by a space.

输出描述

For each test case, print in one line the largest population number and the level of the corresponding generation.  It is assumed that such a generation is unique, and the root level is defined to be 1.

输入例子

23 13
21 1 23
01 4 03 02 04 05
03 3 06 07 08
06 2 12 13
13 1 21
08 2 15 16
02 2 09 10
11 2 19 20
17 1 22
05 1 11
07 1 14
09 1 17
10 1 18

输出例子

9 4

题意解读

首先这题就是一个家族的系谱图,那么数据结构可以理解为一棵树。

对于输入,第一行有两个数,分别代表的是系谱图人总数N(那么成员的id就是1~N,其中1是根节点)和有孩子(有子节点)的人总数M

后面M行每一行代表有孩子的人的id,其孩子个数以及各孩子的id

对于输出,为一行,分别代表最大人数代的代数(根节点为第一代)和 人数(处于该代的节点个数)

根据例子中的数据画出的图。

代码如下

#include<cstdio>
#include<vector>
using namespace std;
vector<int> v[100];
int book[100];//用于存储各个世代的节点数。 
void dfs(int node, int level){
    //遍历带对应世代,就记录其世代数+1
	book[level]++;//该世代的节点数+1 
    //遍历其邻接结点,递归遍历其邻接(子)节点
	for(int i=0;i<v[node].size();i++){
		dfs(v[node][i],level+1);
	} 
}

int main(){
	int N,M,a,b,c,level=1;
    //节点总数以及非叶子节点的个数
	scanf("%d %d",&N,&M);
	for(int i=0;i<M;i++){
		scanf("%d %d",&a,&b);
		for(int j=0;j<b;j++){
			scanf("%d",&c);
            //二维数组,每行的索引代表父节点,该行的元素代表该父节点的子节点集合
			v[a].push_back(c);//建图
		}
	}
    //从根节点开始深度优先遍历
	dfs(1,level);

    //得到是book最大的level,即得到人数最多的世代
	int maxnum = 0,maxlevel=1;
	for(int i=0;i<100;i++){
		if(book[i]>maxnum) {
			maxnum=book[i];
			maxlevel = i; 
		}
	} 
	printf("%d %d",maxnum,maxlevel);
} 

刷题模板总结

void dfs(){
    //核心业务操作,因题而异
    service
    //遍历邻接节点,像树这种结构就是遍历子节点
    for(){
        //对邻接结点进行深层次的遍历,递归调用
        dfs();
    }
}
int main(){
    //根据题目的输入建图,一般使用二维数组存储,推荐使用vector<vector<int>>
    vector<vector<int>> graph;
    //建完图后,选定深度优先遍历的起点.像这种树,一般都是根节点
    //进行深度优先遍历
    dfs();
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值