1072. 树的最长路径
1、树的最长路径:树的最长路径又叫树的直径,即这棵树中距离最远的两个结点的距离。
2、找树的直径的做法:
①任取1点作为起点,找到距离该点最远的一个点u。
②再找到距离点u最远的点v。
③u和v之间的路径就是树的一个直径。
思路:对每个结点,把它能往下走的所有路径枚举一遍,记下最长路径d1和次长路径d2,所以经过这个点的总最长路径长度为=d1+d2。
#include<iostream>
#include<cstring>
using namespace std;
const int N=1e4+10;
int w[N*2],e[N*2],h[N*2],ne[N*2],idx;
int ans;
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int dfs(int u,int father)//这里设置fathe是防止从父节点下来又走到父节点,死循环
{
int d1=0,d2=0;//d1,d2分别记录该点往下走的最长距离和次长距离
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(j==father) continue;
int d=dfs(j,u)+w[i];
if(d>=d1) d2=d1,d1=d;//更新最长距离和次长距离
else if(d>d2) d2=d;
}
ans=max(ans,d1+d2);
return d1;
}
int main()
{
int n;
memset(h,-1,sizeof(h));
scanf("%d",&n);
int a,b,c;
for(int i=0;i<n-1;i++)
{
scanf("%d%d%d",&a,&b,&c);
add(a,b,c),add(b,a,c);
}
dfs(1,-1);
printf("%d\n",ans);
return 0;
}
1073. 树的中心
题意:给定一棵树,树中包含 n 个结点(编号1~n)和 n−1 条无向边,每条边都有一个权值。
请你在树中找到一个点,使得该点到树中其他结点的最远距离最近。
思路:选定一个点,对于该点,它可以往下走(子节点),也可以往上走(父节点)。
①往下走:找出距离当前该点最远的距离d1
②往上走:对于它的父节点,又有两个选择,向上,向下,向下的时候,因为不能又走回来到这个父节点的子节点j,所以要特判一下如果当前向下走的最长路径经过j,那么我们不走这条最长的路径,走次长的路径d2。
与树的直径的区别:他们的思路是一致的,只是树的中心需要把所有点的最长路径和次长路径都计算出来,而树的直径只需要任意选一个点进行计算即可,所以树的中心需要进行两趟dfs,分别是用子节点来更新父节点,用父节点来更新子节点。
#include<iostream>
#include<cstring>
using namespace std;
const int N=1e4+10,INF=0x3f3f3f3f;
int e[N*2],w[N*2],ne[N*2],h[N],idx;
int d1[N],d2[N];//分别存储该节点往下走的最大值和次大值,这两个一定不会经过同一条路径
int up[N],p[N];
int n;
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int dfs_d(int u,int father)
{
d1[u]=d2[u]=-INF;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(j==father) continue;
int d=dfs_d(j,u)+w[i];
if(d>d1[u])
{
d2[u]=d1[u],d1[u]=d;
p[u]=j;//记录这个结点的最长路径是经过哪个点
}
else if(d>d2[u])
d2[u]=d;
}
if(d1[u]==-INF)//u是叶子节点
d1[u]=d2[u]=0;
return d1[u];
}
int dfs_up(int u,int father)
{
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(j==father) continue;
if(p[u]==j) //如果父节点向下走的最长的一条路径经过j,则不能选这条路,要换次长的
up[j]=max(up[u],d2[u])+w[i];
else
up[j]=max(up[u],d1[u])+w[i];
dfs_up(j,u);
}
}
int main()
{
memset(h,-1,sizeof(h));
scanf("%d",&n);
int a,b,c;
for(int i=0;i<n-1;i++)
{
scanf("%d%d%d",&a,&b,&c);
add(a,b,c),add(b,a,c);
}
dfs_d(1,-1);
dfs_up(1,-1);
int res=INF;
for(int i=1;i<=n;i++)
res=min(res,max(d1[i],up[i]));
printf("%d\n",res);
return 0;
}
1075. 数字转换
题意:
思路:每一个数的约数之和一定是确定的,并且是有且只有一个,所以这个数和它的
约数之和是可以有一条边的,所以是可以构成很多棵树,形成一个森林的。
这道题目就变成了找出树中一条最长的路径。
这里注意在求约数之和的时候,不要直接暴力求每个数的约数之和是多少,换一个角度,约数之和是j的数是谁,降低时间复杂度。
#include<iostream>
#include<cstring>
using namespace std;
const int N=5e4+10;
int h[N],e[N],ne[N],idx;
int sum[N];
bool st[N];
int n,ans;
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int dfs(int u,int father)
{
st[u]=true;
int d1=0,d2=0;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(j==father) continue;
int d=dfs(j,u)+1;
if(d>d1)
d2=d1,d1=d;
else if(d>d2)
d2=d;
}
ans=max(ans,d1+d2);
return d1;
}
int main()
{
scanf("%d",&n);
memset(h,-1,sizeof(h));
for(int i=1;i<=n;i++)
for(int j=2;j<=n/i;j++)
sum[i*j]+=i;//记录i*j的约数之和
for(int i=2;i<=n;i++)
if(i>sum[i])
add(sum[i],i);
for(int i=1;i<=n;i++)
if(!st[i])//可能有多棵树
dfs(i,-1);
printf("%d\n",ans);
return 0;
}