P8028 [COCI2021-2022#3] Cijanobakterije
简要题意
给定一个森林,可以在树与树之间连边,不可成环,求最长链。
解析
对于一棵树而言,她本身的最长链自然就是她的直径。
贪心的想,把所有的树的直径全部连接起来就是最长的那一条链。
所以,对于每一棵树求出她的直径然后求和就好了。
特别注意,这题的长度是指节点的个数,因此比边数大 。
代码实现
树的直径有两种求法:两遍搜索和树形 DP。
前者可以保存遍历路径但无法处理负边权,后者则正好反之。
搜索
先选树上任意一点 ,然后找到距离 最远的点 ,之后再以 为起点,找到离她最远的点 。最后 与 之间的距离即为该树的直径。
理论上 DFS 与 BFS 均可,但写 DFS 的似乎更多。
DP
别的题解讲的还挺明白的,而且本题也没有负边权我不会,这里就不放了。
此外,树的直径还有不少性质,这里贴个链接,如需自取。
终于来到了喜闻乐见的代码环节。
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+12;
int n,m;
int head[maxn],cnt=0;
struct node {
int nxt,to;
} edge[maxn];
bool vis[maxn],vis2[maxn];
int ans=0,o=0,dis=0;
void add(int u,int v) {
cnt++;
edge[cnt].to=v;
edge[cnt].nxt=head[u];
head[u]=cnt;
}
void dfs1(int u,int d) {
vis[u]=1;
if(d+1>dis) {
dis=d+1;
o=u;
}
for(int i=head[u]; i; i=edge[i].nxt) {
int v=edge[i].to;
if(!vis[v])
dfs1(v,d+1);
}
}
void dfs2(int u,int d) {//可以跟dfs1和到一起的,不过容易出锅,模拟赛100->30。
vis2[u]=1;
dis=max(dis,d+1);
for(int i=head[u]; i; i=edge[i].nxt) {
int v=edge[i].to;
if(!vis2[v]) {
dfs2(v,d+1);
}
}
}
int main() {
cin>>n>>m;
for(int i=1; i<=m; i++) {
int u,v;
cin>>u>>v;
add(u,v);
add(v,u);
}
for(int i=1; i<=n; i++) {
if(!vis[i]) {
dis=0;//记得清零
dfs1(i,0);
dfs2(o,0);
ans+=dis;
}
}
cout<<ans<<endl;
return 0;
}