(图论)给定一棵树,找到一个点,使得与其他节点距离总和最小值

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(图论)给定一棵树,找到一个点,使得与其他节点距离总和最小值相关的知识,希望对你有一定的参考价值。

如题,求解题思路,如果有pascal代码加分
附一个具体问题:
【问题】
有一个村庄居住着n个村民,有n-1条路径使得这n个村民的家联通,每条路径的长度都为1。现在村长希望在某个村民家中召开一场会议,村长希望所有村民到会议地点的距离之和最小,那么村长应该要把会议地点设置在哪个村民的家中,并且这个距离总和最小是多少?若有多个节点都满足条件,则选择节点编号最小的那个点。

参考技术A 有图么?、 没图的话。。。。。。。。。额追问

补充个样例 样例的图就是一条直线1——2——3——4 这样的
【输入格式】
第一行。一个数n,表示有n个村民。
接下来n-1行,每行两个数字a和b,表示村民a的家和村民b的家之间存在一条路径。
【输出格式】
一行输出两个数字x和y
x表示村长将会在哪个村民家中举办会议
y表示距离之和的最小值
【样例输入】
4
1 2
2 3
3 4
【样例输出】
2 4

追答

你上的什么学。。。。完全看不懂

追问

这属于信息竞赛,看不懂就算了

参考技术B 思路:树的重心,然后dfs求每个点到ans的距离。
#include<cstdio>
#include<algorithm>
#define maxn 50001
using namespace std;
int deep[maxn],tot,n,ans,head[maxn],s[maxn],size=(1<<30);
bool vis[maxn];
struct node

int to,next;
a[maxn*2];
void add(int x,int y)

tot++;
a[tot].to=y;
a[tot].next=head[x];
head[x]=tot;

void dfs(int x)

vis[x]=1;
s[x]=0;
int tmp=0;
for(int i=head[x];i;i=a[i].next)

int y=a[i].to;
if(!vis[y])

dfs(y);
s[x]+=s[y]+1;
tmp=max(tmp,s[y]+1);


tmp=max(tmp,n-s[x]-1);
if(tmp<size||tmp==size&&x<ans)

ans=x;
size=tmp;


void dfs1(int x,int y,int d)

deep[x]=d;
for(int i=head[x];i;i=a[i].next)
if(y!=a[i].to)
dfs1(a[i].to,x,d+1);

int main()

int i,j,x,y;
scanf("%d",&n);
for(i=1;i<n;i++)
scanf("%d%d",&x,&y),add(x,y),add(y,x);
dfs(1);
dfs1(ans,ans,0);
int sum=0;
for(i=1;i<=n;i++)
sum=sum+abs(deep[i]-deep[ans]);
printf("%d %d",ans,sum);
return 0;
参考技术C 网上自行搜索克鲁斯卡尔算法或者普利姆算法即可

求树上每个节点可以走到的最远距离

1073. 树的中心

给定一棵树,树中包含 n 个结点(编号1~n)和 n−1 条无向边,每条边都有一个权值。
请你在树中找到一个点,使得该点到树中其他结点的最远距离最近。
输入格式
第一行包含整数 n。
接下来 n−1 行,每行包含三个整数 ai,bi,ci,表示点 ai 和 bi 之间存在一条权值为 ci 的边。
输出格式
输出一个整数,表示所求点到树中其他结点的最远距离。
数据范围
1≤n≤10000,
1≤ai,bi≤n,
1≤ci≤105
输入样例:
5
2 1 1
3 2 1
4 3 1
5 1 1
输出样例:
2

第一次DFS求每个节点通过直接点的最远距离max1以及该儿子,并且还需要求一个次远距离max2。然后再求通过父节点可以到达的最远距离max3,可以是合并父节点的max1或max2或max3,但是当父节点的max1的儿子是该节点时,就不能合并,只能尝试合并max2,这就是记录次远距离的作用

#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int head[N],cnt,ans;
struct eg{
    int v,c,nex;
}edge[N*2];
int Max1[N],Max2[N],Max3[N];
int son1[N];
void addedge(int u,int v,int c){
    edge[cnt]=(eg){v,c,head[u]};
    head[u]=cnt++;
}
void dfs1(int u,int pre){
    for(int i=head[u];~i;i=edge[i].nex){
        int v=edge[i].v,c=edge[i].c;
        if(pre==v) continue;
        dfs1(v,u);
        if(c+Max1[v]>=Max1[u]){
            Max2[u]=Max1[u];
            Max1[u]=c+Max1[v];
            son1[u]=v;
        }
        else if(c+Max1[v]>Max2[u]){
            Max2[u]=c+Max1[v];
        }
    }
    //cout<<u<<" "<<Max1[u]<<" "<<son1[u]<<"--"<<Max2[u]<<" "<<son2[u]<<endl;
}
void dfs2(int u,int pre){
    for(int i=head[u];~i;i=edge[i].nex){
        int v=edge[i].v,c=edge[i].c;
        if(pre==v) continue;

        if(son1[u]!=v){
            Max3[v]=Max1[u]+c;
        }
        else {
            Max3[v]=Max2[u]+c;
        }
        Max3[v]=max(Max3[u]+c,Max3[v]);
        dfs2(v,u);
    }
}
int main(){
    int n;
    cin>>n;
    memset(head,-1,sizeof head);
    for(int i=1,u,v,c;i<n;++i){
        cin>>u>>v>>c;
        addedge(u,v,c);
        addedge(v,u,c);
    }
    dfs1(1,0);
    dfs2(1,0);
    int ans=1000000000;
    for(int i=1;i<=n;++i)
        ans=min(ans,max(Max1[i],max(Max2[i],Max3[i])));
    cout<<ans<<endl;
    return 0;
}

325. 计算机

一所学校前一段时间买了第一台计算机(所以这台计算机的ID是1)。
近年来,学校又购买了N-1台新计算机。
每台新计算机都与之前买进的计算机中的一台建立连接。
现在请你求出第i台计算机到距离其最远的计算机的电缆长度。

例如,上图中距离计算机1最远的是计算机4,因此 S1=3;距离计算机2最远的是计算机4和5,因此 S2=2;距离计算机3最远的是计算机5,所以 S3=3;同理,我们也得到 S4=4,S5=4。
输入格式
输入包含多测试数据。
每组测试数据第一行包含整数N。
接下来N-1行,每行包含两个整数,第 i 行的第一个整数表示第 i 台电脑买入时连接的电脑编号,第二个整数表示这次连接花费的电缆长度。
输出格式
每组测试数据输出N行。
第i行输出第i台电脑的Si。
数据范围
1≤N≤10000,
电缆总长度不超过109
输入样例:
5
1 1
2 1
3 1
1 1
输出样例:
3
2
3
4
4

带上边权

#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int head[N],cnt,ans;
struct eg{
    int v,c,nex;
}edge[N*2];
int Max1[N],Max2[N],Max3[N];
int son1[N],son2[N];
void addedge(int u,int v,int c){
    edge[cnt]=(eg){v,c,head[u]};
    head[u]=cnt++;
}
void dfs1(int u,int pre){
    for(int i=head[u];~i;i=edge[i].nex){
        int v=edge[i].v,c=edge[i].c;
        if(pre==v) continue;
        dfs1(v,u);
        if(c+Max1[v]>=Max1[u]){
            Max2[u]=Max1[u];
            son2[u]=son1[u];
            Max1[u]=c+Max1[v];
            son1[u]=v;
        }
        else if(c+Max1[v]>Max2[u]){
            Max2[u]=c+Max1[v];
            son2[u]=v;
        }
    }
    //cout<<u<<" "<<Max1[u]<<" "<<son1[u]<<"--"<<Max2[u]<<" "<<son2[u]<<endl;
}
void dfs2(int u,int pre){
    for(int i=head[u];~i;i=edge[i].nex){
        int v=edge[i].v,c=edge[i].c;
        if(pre==v) continue;

        if(son1[u]!=v){
            Max3[v]=Max1[u]+c;
        }
        else {
            Max3[v]=Max2[u]+c;
        }
        Max3[v]=max(Max3[u]+c,Max3[v]);
        dfs2(v,u);
    }
}
int main(){
    int n;
    cin>>n;
    memset(head,-1,sizeof head);
    for(int i=2,u,v,c;i<n;++i){
        cin>>u>>v>>c;
        addedge(u,v,c);
        addedge(v,u,c);
    }
    dfs1(1,0);
    dfs2(1,0);
    int ans=1000000000;
    for(int i=1;i<=n;++i)
        ans=min(ans,max(Max1[i],max(Max2[i],Max3[i])));
    cout<<ans<<endl;
    return 0;
}

齐心抗疫


由于最终的答案一定是某个点a被另一个点b帮助,距离a最远的点就一定是b,且b点值一定小于a,如果大于a,那么答案就应该是b点值乘与一个距离大于等于a的距离,答案就会更大。所以只要找到每个点的可以走到的最远距离乘再与他的点值像乘法,就能够找到点a并更新答案

#include<bits/stdc++.h>
using namespace std;
const int N=50010;
int fw[N],h[N],tot,max1[N],max2[N],son1[N],p[N];
struct eg{
    int v,nex;
}e[N*2];
void add(int u,int v){
    e[tot]=(eg){v,h[u]};
    h[u]=tot++;
}
void dfs1(int u,int pre){
    son1[u]=u;
    max1[u]=max2[u]=0;
    for(int i=h[u];~i;i=e[i].nex){
        int v=e[i].v,c=1;
        if(pre==v) continue;
        dfs1(v,u);
        if(max1[v]+c>max1[u]){
            max2[u]=max1[u];
            max1[u]=max1[v]+c;
            son1[u]=son1[v];
        }
        else if(max1[v]+c>max2[u]){
            max2[u]=max1[v]+c;
        }
    }
}
void dfs2(int u,int pre){
    for(int i=h[u];~i;i=e[i].nex){
        int v=e[i].v,c=1;
        if(v==pre) continue;
        fw[v]=0;
        if(son1[u]!=son1[v]&&fw[v]<max1[u]+c){
            fw[v]=max1[u]+c;
        }
        else if(fw[v]<max2[u]+c){
            fw[v]=max2[u]+c;
        }
        if(fw[v]<fw[u]+c){
            fw[v]=fw[u]+c;
        }
        dfs2(v,u);
    }
}
int main(){
    int n;
    memset(h,-1,sizeof h);
    cin>>n;
    for(int i=1;i<=n;++i) cin>>p[i];
    for(int i=2;i<=n;++i){
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);
        add(v,u);
    }
    dfs1(1,0);
    dfs2(1,0);
    int res=0;
    for(int i=1;i<=n;++i){
        res=max(res,p[i]*max(max1[i],fw[i]));
    }
    cout<<res<<endl;
    return 0;
}

以上是关于(图论)给定一棵树,找到一个点,使得与其他节点距离总和最小值的主要内容,如果未能解决你的问题,请参考以下文章

1073. 树的中心(树形dp)

1073. 树的中心(树形dp)

[WC2019] 通道

AcWing. 1073 树的中心

ZOJ 3949 Edge to the Root

最短路径树