树的最长路径带权值
树的直径可能时红色的边
从上图可以看出,每次要两个变量存放以u为根,最长路径d1,和次长路径d2,那么整个树的最长路径就有可能是d1+d2
我们每次要返回以u为根的贯穿试的最长路径,给他的父节点判断使用如下图
#include <bits/stdc++.h>
#define ll long long
#define INF 0x7f7f7f7f
using namespace std;
const int N=1e4+10,M=5;
int n,m,ecnt,ans;
int head[N];
struct edge{
int u,v,w,nxet;
}E[N<<1];
void add(int u,int v,int w){
E[++ecnt].u=u;
E[ecnt].v=v;
E[ecnt].w=w;
E[ecnt].nxet=head[u];
head[u]=ecnt;
}
int dfs(int u,int fa){
//dist记录以u为根贯穿试最长路径
int dist=0,d1=0,d2=0;//d1和d2记录以u为根路径最长和次长值
//由于路径可以只包含一个点所以一定不为负数
for(int i=head[u];i;i=E[i].nxet){
int v=E[i].v;
if(v==fa)continue;//不能回头
int d=dfs(v,u)+E[i].w;//子树v到u的权值
dist=max(dist,d);//以u为根的树的最长路径
if(d>=d1)d2=d1,d1=d;
else if(d>d2)d2=d;
}
ans=max(ans,d1+d2);//d1+d2才是树的直径
return dist;//将以u为根向下纵向穿的最大路线返回
}
int main() {
cin>>n;
for(int i=1;i<=n-1;i++){
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);
}
dfs(1,-1);
cout<<ans;
return 0;
}
树的中心
我们要找出一个点到各个点中的最长距离,然后求出整个树所有点中最长距离最短,我们把重心放在最长距离上,树上一个点最长距离无非是向上最长或向下最长距离
向下最长,我们上一道题求树的直径中求出了u点的最长路径,和次长路径那么向下最长一定是u点向下dfs得出的最长路径所以求法和上一道题目相同。
向上最长,这个要分析当前节点紧挨着的父节点,无非两种情况,第一种是其父节点向上最长,第二种是其父节点向下最长那么第二种情况需要讨论,如果父亲节点最长距离d1、次长距离d2,其中d1如果不经过儿子节点,那么儿子节点向上最长一定是max(up[父亲],d1)+w*(父亲儿子之间权值)*,如果d1经过儿子节点,那么d1无法使用,只能使用d2那么儿子向上最长就是max(up[父亲],d2)+w。
(别忘记加上儿子和父亲间的权值)
#include <bits/stdc++.h>
#define ll long long
#define INF 0x7f7f7f7f
using namespace std;
const int N=1e5+10,M=5;
int n,m,ecnt,ans;
int head[N];
int d1[N],d2[N],up[N];//记录向下最长直径、次长直径、向上最长直径
int p1[N],p2[N];//记录u最长直径经过的第一个节点,和次长直径经过的第一个节点
struct edge {
int u,v,w,next;
} E[N<<1];
void add(int u,int v,int w) {
E[++ecnt].u=u;
E[ecnt].v=v;
E[ecnt].w=w;
E[ecnt].next=head[u];
head[u]=ecnt;
}
int dfs_down(int u,int fa) {
d1[u]=-INF,d2[u]=-INF;
for(int i=head[u]; i; i=E[i].next) {
int v=E[i].v;
if(v==fa)continue;//一直向下
int dist=dfs_down(v,u)+E[i].w;
if(dist>d1[u]) {
d2[u]=d1[u],d1[u]=dist;//更新
p2[u]=p1[u],p1[u]=v;
} else if(dist>d2[u]) {
d2[u]=dist;
p2[u]=v;
}
}
if(d1[u]==-INF)d1[u]=d2[u]=0;//叶子节点
return d1[u];
}
void dfs_up(int u,int fa) {
//根节点不用特殊处理,根节点的up为0
for(int i=head[u]; i; i=E[i].next) {
int v=E[i].v;
if(v==fa)continue;
if(p1[u]==v) { //u最长直径,经过v
up[v]=max(up[u],d2[u])+E[i].w;//更新v向上的最长路径
} else {
up[v]=max(up[u],d1[u])+E[i].w;
}
dfs_up(v,u);//继续向下处理
}
}
int main() {
cin>>n;
for(int i=1; i<=n-1; i++) {
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);
}
dfs_down(1,-1);
dfs_up(1,-1);
ans=INF;
for(int i=1; i<=n; i++)ans=min(ans,max(d1[i],up[i]));
cout<<ans<<endl;
return 0;
}
数字转换
每一个数字和对应的约数和,可以链接一条边,这样依次类推就构成了一棵树,它要求转换步数最多,那么也就是求这颗树的直径(当然了可能存在多颗树,所以要用一个数组vis记录那些是根节点)
在求约数和时采用了比较新颖的做法
#include <bits/stdc++.h>
#define ll long long
#define INF 0x7f7f7f7f
using namespace std;
const int N=5e4+10;
int n,m,ecnt,ans;
int head[N];
int sum[N],vis[N];//sum记录i的约数和,vis记录i是否为树根
struct edge {
int u,v,w,next;
} E[N<<1];
void add(int u,int v,int w) {
E[++ecnt].u=u;
E[ecnt].v=v;
E[ecnt].w=w;
E[ecnt].next=head[u];
head[u]=ecnt;
}
//求树的直径
int dfs(int u) {
int d1=0,d2=0;
for(int i=head[u]; i; i=E[i].next) {
int v=E[i].v;
int dist=dfs(v)+E[i].w;
if(dist>=d1) {
d2=d1,d1=dist;
} else if(dist>d2) {
d2=dist;
}
}
ans=max(ans,d1+d2);
return d1;
}
int main() {
cin>>n;
for(int i=1; i<=n; i++) {
for(int j=2; j<=n/i; j++) { //i*j<=n 除过去防止溢出
sum[i*j]+=i;//i*j的约数一定有i
}
}
for(int i=2; i<=n; i++) {
if(sum[i]<i) {
add(sum[i],i,1);//约数和小于本身添加边
vis[i]=1;//i不为树根
}
}
for(int i=1; i<=n; i++) {
if(!vis[i])//没被标记就为树根
dfs(i); //1不存在,所以从2开始
}
cout<<ans<<endl;
return 0;
}
#include <bits/stdc++.h>
#define ll long long
#define INF 0x7f7f7f7f
using namespace std;
const int N=100+10;
int n,m,ecnt,ans;
int head[N];
int f[N][N];
struct edge {
int u,v,w,next;
} E[N<<1];
void add(int u,int v,int w) {
E[++ecnt].u=u;
E[ecnt].v=v;
E[ecnt].w=w;
E[ecnt].next=head[u];
head[u]=ecnt;
}
void dfs(int u,int fa){
//分组背包(按支的个数分组)
for(int i=head[u];i;i=E[i].next){
int v=E[i].v;
if(v==fa)continue;
dfs(v,u);
for(int j=m;j>=0;j--){//从后向前遍历,因为需要f[u][j-k-1]的值,这个值不能更新
for(int k=0;k<j;k++)
f[u][j]=max(f[u][j],f[u][j-k-1]+f[v][k]+E[i].w);
}
}
}
int main() {
cin>>n>>m;
for(int i=1;i<=n-1;i++){
int u,v,w;
cin>>u>>v>>w;
add(u,v,w);
add(v,u,w);
}
dfs(1,-1);
cout<<f[1][m]<<endl;
return 0;
}