【L2-038 病毒溯源】天梯赛L2详解

天梯赛-L2-038 病毒溯源

题目详情:在这里插入图片描述在这里插入图片描述

在这里插入图片描述

思路:

这个题目是一个遍历的题目,不管是深度优先遍历,还是广度优先遍历,都是可以写出来的。我采用vector容器作为邻接表存放数据,那么需要解决几个问题?
1、如何找到根节点,如何解决最小序列问题
答:找到根节点很简单,我们每次输入数据的时候,把所有出现的节点都标记为1,而没有被标记为1的节点肯定就是我们要找的根节点了。解决最小序列:只需要我们每次都输入完一组数据后直接用sort排序就可以了。

2、在遍历的同时,不仅需要我们同时记住层数,还需要把最深的路径记录下来。
答:这个问题容易解决,我们用vector容器v[maxn]容器存放数据,在DFS过程中,如果我们找到更深的,那么我们就把当前的容器的v[i]赋给另外一个vector容器temp。让temp一直存放最深的数据。而temp的大小,就是我们要求的层数了。一举两得!

3、同时本题还要进行回溯。为什么呢?
答:首先,我们要什么是回溯?回溯非常简单:当你在遍历二叉树时,B和C是A的节点,你遍历B之后,需要回到A,接着遍历C,B回到A的这个过程就回溯。回溯就是要求我们把所有情况都枚举出来,在这之中找到我们要的答案。像图的深度优先遍历以及二叉树找节点的距离就不需要回溯,因为本身就是要遍历所有节点,而不是找到所有情况。
那么什么情况下需要回溯呢?
需要我们不断尝试,把所有情况都列举出来,才可以找到答案的时候。就比如本题,为什么要回溯?DFS是将所有节点都遍历一遍,如果不回溯,那么,最后的结果就会是所有的节点全部存放在temp中。
所以,必须要回溯,每次判断完节点深度的时候,都需要返回父节点去判断另外节点的深度。如果这里不是很理解,看一下代码就可以了。

详细代码:

#include<bits/stdc++.h>
using namespace std;
int n;
const int maxn=10005;
vector<int>v[maxn];//邻接表存放所有数据
vector<int>temp, ans;//temp存放符合条件的序列,
//ans存放根节点:然后将所有孩子节点逐个放入,边回溯边遍历
int root[maxn];//找根节点
void Dfs(int index)//孩子下标为index
{
	if(ans.size()>temp.size())//找到更深的,更新temp
    {
		temp.clear();//每次更新temp,需要先把temp清空,防止其他的情况
		temp=ans;
	}
	for(int i=0; i<v[index].size(); i++)//标号为index的孩子节点
    {
		ans.push_back(v[index][i]);//先将孩子节点入p
		Dfs(v[index][i]);//深搜孩子节点
		ans.pop_back(); //回溯
		//这里可以把回溯去掉,然后你看看是什么情况,将会是所有的节点都会放入temp中
	}
}
int main()
{
	cin>>n;
	for(int i=0; i<n; i++)
    {
		int k;
		cin>>k;
		while(k--)//当k等于0 时,就不用考虑
        {
			int x;
			cin>>x;
			v[i].push_back(x);
			root[x]=1;//将所有的孩子节点的root都设为1,这样只需要找到不是1的节点,那个节点就是根节点了
		}
		if(v[i].size())
        {
			sort(v[i].begin(),v[i].end());//每次放完孩子都需要排序,以求得最小的编号
			//这是排序是将最小节点的孩子,放在了父节点的左边,即开始遍历的时候,肯定是从左边先遍历,然后这个时候如果两个序列一样长,那么序列小的肯定先被放入temp中,序列大的就不会被放进去。
		}
	}
	for(int i=0; i<n; i++)
    {
		if(!root[i])//找到根节点进行深搜
        {
			ans.push_back(i);
			Dfs(i);//从根节点开始搜索,每次更新ans
			break;
		}
	}
	cout<<temp.size()<<endl;
	for(int i=0; i<temp.size(); i++)
    {
		if(!i) 
            cout<<temp[i];
		else
            cout<<" "<<temp[i];
	}
}

知识总结:

1、这里面用到的知识点:回溯,DFS,vector作为邻接表。DFS在天梯赛题目中还是非常多的一定要多多练习啊。DFS说白了,就是递归,好好学习,天天向上!
最后附上用vector作为邻接表存图的代码,不会这么用的小伙伴可以看一下:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 10005;
vector<int> E[maxn];
int n,m;//结点数,边数 

void createVec()//用vector存储无向图 
{
	int u,v;
	cin>>n>>m;
	for(int i = 0; i < m; i++)//m为边数 
	{
		cin>>u>>v;//一条边的两个结点编号 
		E[u].push_back(v);
		E[v].push_back(u);
	}
}

void printg()//输出邻接表 
{
	cout<<"图的邻接表为:"<<endl; 
	for(int u = 0; u < n; u++)
	{
		cout<<u<<"-->";
		for(int i = 0; i < E[u].size(); i++)//访问u的所有邻接点 
		{
			int v = E[u][i];
			cout<<"["<<v<<"]\t"; 
		}
		cout<<endl;
	}
}

int main()
{
	createVec();
	printg();
	return 0;
}
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值