L2-038 病毒溯源(好题)
解题思路
本题显然使用 邻接表 \(+\) DFS
由于本题需要存路径,所以需要一个ne数组来记录路径,还需要记录每个人的父节点,因为根节点无父节点,最后要从根往下dfs。
为了最后能输出最小的排序,需要在每个节点的孩子输入完之后对其进行排序,这样就能保证每一层都是从小到大,而在所有的最长链当中,最先找到的一定就是最小的链。
本题的DFS函数是个难点
为了防止路径被更新,需要记录每个节点的最大深度,只有当发现更深的情况再更新。本题采用的DFS函数是先走到尽头,再往上更新深度。
ac✅️代码
算法一(思路不好想)
#include<iostream>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
vector<int> children[10010];
int parent[10010];
int max_h[10010];
int ne[10010];
int n;
//这里的DFS函数的作用是计算每个节点的最大深度,父节点的最大深度等于子节点的最大深度 加 1。
所以先调用dfs,遍历计算出根节点的每一个子节点的最大深度,然后遍历子节点,找到最大值
void dfs(int u)
{
max_h[u] = 1;
ne[u] = -1;
for(auto v : children[u])
{
dfs(v);
if(max_h[v] + 1 > max_h[u])//主要是这里
{
max_h[u] = max_h[v] + 1;
ne[u] = v;
}
}
}
int main()
{
memset(parent , -1 , sizeof parent);
cin>>n;
for(int i = 0 ; i < n ; i++)
{
int t;cin>>t;
for(int j = 0 ; j < t ; j ++)
{
int x;cin>>x;
parent[x] = i;
children[i].push_back(x);
}
sort(children[i].begin() , children[i].end());
//
}
int root ;
for(int i = 0 ; i < n ; i++)
{
if(parent[i] == -1 ) root = i;
}
dfs(root);
cout<<max_h[root]<<endl;
cout<<root;
while(ne[root] != -1)
{
cout<<" "<<ne[root];
root = ne[root];
}
return 0;
}
算法一的改良版(比较好理解)
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
vector<int> children[100010];
int parent[100010];
int max_h[100010];
int ne[100010];
int n;
int dfs(int u)
{
if (max_h[u] != 0)
{
return max_h[u];
}
if (children[u].empty())
{
ne[u] = -1;
return max_h[u] = 1;
}
int current_max = 1;
int best_child = -1;
for (int v : children[u])
{
int child_h = dfs(v);
int path_through_v = child_h + 1;
// 决策:只有大于才更新,配合前面的 sort 保证了字典序最小
if (path_through_v > current_max)
{
current_max = path_through_v;
best_child = v;
}
}
ne[u] = best_child;
max_h[u] = current_max;
return max_h[u]
}
int main()
{
memset(parent, -1, sizeof parent);
memset(max_h, 0, sizeof max_h);
if (!(cin >> n)) return 0;
for (int i = 0; i < n; i++)
{
int k;
cin >> k;
for (int j = 0; j < k; j++)
{
int x;
cin >> x;
parent[x] = i;
children[i].push_back(x);
}
sort(children[i].begin(), children[i].end());
}
int root = 0;
for (int i = 0; i < n; i++)
{
if (parent[i] == -1)
{
root = i;
break;
}
}
dfs(root);
cout << max_h[root] << endl;
cout << root;
int curr = root;
while (ne[curr] != -1)
{
curr = ne[curr];
cout << " " << curr;
}
cout << endl;
return 0;
}
算法二(简单很多,非常直观 , 但是很容易被卡掉)
- 这种代码被卡,与 CF 2185D题一样,我刚开始是用两个vector数组,然后用其中一个直接赋值给
另一个,思路是对的,但是超时了,甚至它的优化方法与本题也是几乎一样
天梯赛的数据还是太友好了
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
vector<int> temp_path;
vector<int> best_path;
vector<int> children[10010];
int parent[10010];
void dfs(int u)
{
temp_path.push_back(u);
if(!children[u].size())
{
if(temp_path.size() > best_path.size())
{
best_path = temp_path;
}
}
else//这里的else不要也行
{
for(int v : children[u])
{
dfs(v);//由于这里修改是全局数组,需要回溯
}
}
temp_path.pop_back();
}
int main()
{
memset(parent,-1,sizeof parent);
int n;cin>>n;
for(int i = 0 ; i < n ;i ++)
{
int x;cin>>x;
for(int j = 0 ; j < x ; j ++)
{
int y ; cin>>y;
parent[y] = i;
children[i].push_back(y);
}
sort(children[i].begin() , children[i].end());
}
int root = 0 ;
for(int i = 0 ; i < n ; i++) if(parent[i] == -1) root = i;
dfs(root);
cout<<best_path.size()<<endl;
for(int i = 0 ; i < best_path.size() ; i ++)
{
cout<<best_path[i];
if(i != best_path.size() - 1) cout<<" ";
}
return 0;
}

浙公网安备 33010602011771号