nowcoder 桃花 (两次DFS求直径)

本文介绍了一个有趣的算法问题:如何利用一次魔法棒操作从一棵代表桃花的树中获取最多的节点(桃花)。通过深度优先搜索(DFS)两次找到树中最长路径的一端开始,再进行一次DFS以确定整棵树最长路径长度,即为最多可获取的桃花数量。

桃花
题目描述

桃花一簇开无主,可爱深红映浅红。
——《题百叶桃花》
桃花长在桃树上,树的每个节点有一个桃花,调皮的HtBest想摘尽可能多的桃花。HtBest有一个魔法棒,摘到树上任意一条链上的所有桃花,由于HtBest法力有限,只能使用一次魔法棒,请求出Htbest最多可以摘到多少个桃花。

输入描述:

第一行有一个正整数n,表示桃树的节点个数。
接下来n-1行,第i行两个正整数ai,bi ,表示桃树上的节点ai,bi之间有一条边。

输出描述:

第一行一个整数,表示HtBest使用一次魔法棒最多可以摘到多少桃花。

示例1
输入

3
1 2
2 3

输出

3

示例2
输入

4
1 2
2 3
3 4

输出

4

备注

对于100%的测试数据:
1 ≤ n ≤ 1000000
数据量较大,注意使用更快的输入输出方式。

#include<iostream>
#include<cstring>
#include<stack>
#include<queue>
#include<cstdio>
#include<vector>
#include<string>
#include<map>
#include<unordered_map>
#include<algorithm>
#include<set>
using namespace std;


const int maxn = 1e6 + 50;
const int MOD = 1e9 + 7;
const double eps = 1e-9;


#define PI acos(-1)
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define mem(a,b) memset(a,b,sizeof(a))
#define rep(i,x) for(int i=0;i<x;++i)
#define UP(i,x,y) for(int i=x;i<=y;i++) 
#define DOWN(i,x,y) for(int i=x;i>=y;i--)
#define DEBUG(x)  cout << "#: " << (x) << endl

using LL = long long;
using PII = pair<int,int>;
using VI = vector<int>;

VI G[maxn];
bool vis[maxn];
int n,ans,idx,cur;

void dfs(int u,int now){
    vis[u] = true;
    if(now > ans){
        ans = now;
        idx = u;
    }
    for(auto v : G[u]){
        if(!vis[v]){
            dfs(v,now + 1);
        }
    }
}   

int main(){
    IO;
    cin >> n;
    int u,v;
    for(int i = 0; i < n - 1; i++){
        cin >> u >> v;
        G[u].push_back(v); G[v].push_back(u);
    }
    ans = 0;
    dfs(1,0);
    ans = 0;
    mem(vis,false);
    dfs(idx,0);
    cout << ans + 1 << endl;
    return 0;
}
<think> 我们正在讨论图的遍历问题。根据引用内容,特别是引用[2]和[3],我们可以知道: - 图的深度优先遍历(DFS)在遇到非连通图时,一次DFS调用只能遍历完起始顶点所在的连通分。 - 为了遍历整个图,我们需要检查是否还有顶点未被访问,如果有,则选择另一个未被访问的顶点作为起始点再次调用DFS,直到所有顶点都被访问。 题目中说“调用两次DFS才完成对图的遍历”,这意味着在遍历过程中,我们调用了两次DFS函数(即从两个不同的起始点开始DFS)才访问了所有顶点。 根据上述引用[2]和[3]的内容,我们可以推断该图具有以下性质: 1. **非连通图**:因为一次DFS只能遍历一个连通分,所以两次DFS意味着图至少有两个连通分。 2. **连通分的数**:由于调用了两次DFS,说明图中有两个连通分(因为每次DFS遍历一个连通分两次恰好遍历完)。 但是注意,也有可能存在多于两个连通分吗?实际上,如果图有三个连通分,那么我们需要调用三次DFS,即调用DFS的次数等于连通分的个数。因此,调用两次DFS说明图恰好有两个连通分。 另外,引用[3]明确指出:“在无向图中,广度优先和深度优先算法中,都有一个每个节点都调用一次深度或广度优先遍历算法的循环,若从1开始,调用一次可遍历到1234567节点,然后234567不调用遍历算法;到节点9时,再次调用算法可遍历到89,然后9不调用遍历算法。所以,有几个连通分,就会调用几次遍历算法”。 因此,我们可以得出结论:调用两次DFS完成遍历的图是一个具有两个连通分的非连通图。 注意:这里我们默认讨论的是无向图,因为有向图的情况不同(如强连通分等),但题目中没有特别指明,而引用中主要讨论的是无向图。不过,即使是有向图,如果是非强连通图,也可能需要多次调用DFS(但通常我们讨论的是无向图的连通分)。根据引用[3]的注释,该结论在无向图中成立,在有向图中不一定成立。因此,题目中提到的图应该是无向图。 综上所述,该图的性质是:它是一个无向非连通图,且恰好有两个连通分。 §§生成相关问题§§ </think> ### 调用两次DFS完成遍历的图性质分析 当图的深度优先遍历(DFS)需要调用两次才能完成全部遍历时,该图必然具备以下关键性质: 1. **非连通图**(Disconnected Graph) 图中至少存在**两个互不连通的子图**(即连通分)。第一次DFS调用只能遍历起始顶点所在的连通分,第二次调用才能访问另一个独立的连通分。 数学表示:设图$G=(V,E)$,存在顶点划分$V = V_1 \cup V_2$满足: - $V_1 \cap V_2 = \emptyset$ - $\forall u \in V_1, v \in V_2$, $(u,v) \notin E$ 此时DFS需调用两次[^2][^3]。 2. **连通分为2** 调用DFS的次数严格等于连通分的数。调用两次DFS说明图**恰好包含两个连通分**[^3]。 示例拓扑: ```mermaid graph LR A---B C---D ``` 第一次DFS遍历{A,B},第二次遍历{C,D}。 3. **无向图特性**(Undirected Graph) 此性质仅在无向图中成立。有向图即使是非强连通的,调用次数也可能不等于强连通分数(需用Kosaraju等专用算法)[^3]。 ### 验证示例 ```python # 两个独立连通分的图(A-B 和 C-D) graph = { 'A': ['B'], 'B': ['A'], # 第一个连通分 'C': ['D'], 'D': ['C'] # 第二个连通分 } visited = set() DFS_count = 0 def DFS(node): global visited visited.add(node) for neighbor in graph[node]: if neighbor not in visited: DFS(neighbor) # 需要调用两次DFS if 'A' not in visited: DFS('A') # 第一次调用 → 遍历A,B DFS_count += 1 if 'C' not in visited: DFS('C') # 第二次调用 → 遍历C,D DFS_count += 1 print(f"调用次数: {DFS_count}") # 输出: 2 ``` ### 对比:连通图只需一次DFS 若图连通(单连通分),只需调用一次DFS即可遍历所有顶点: ```mermaid graph LR A---B---C ``` ```python 连通图 = {'A':['B'], 'B':['A','C'], 'C':['B']} # 只需调用一次DFS('A')即可访问所有顶点 ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值