洛谷P1364 医院设置(树形DP/二次扫描与换根法)

题目描述

设有一棵二叉树,如图:

其中,圈中的数字表示结点中居民的人口。圈边上数字表示结点编号,现在要求在某个结点上建立一个医院,使所有居民所走的路程之和为最小,同时约定,相邻接点之间的距离为 111。如上图中,若医院建在1 处,则距离和 =4+12+2×20+2×40=136=4+12+2\times20+2\times40=136=4+12+2×20+2×40=136;若医院建在 333 处,则距离和 =4×2+13+20+40=81=4\times2+13+20+40=81=4×2+13+20+40=81。

输入格式

第一行一个整数 nnn,表示树的结点数。

接下来的 nnn 行每行描述了一个结点的状况,包含三个整数 w,u,vw, u, vw,u,v,其中 www 为居民人口数,uuu 为左链接(为 000 表示无链接),vvv 为右链接(为 000 表示无链接)。

输出格式

一个整数,表示最小距离和。

输入输出样例

输入 #1
5						
13 2 3
4 0 0
12 4 5
20 0 0
40 0 0
输出 #1
81
首先注意到这个题的数据范围很小,n只有1e2,因此枚举每个节点进行DFS可以通过,复杂度为O(n^2)。
考虑更高效的方法,受到题解区以及蓝书的启发,对于这种没有明显树根的题,可以采用二次扫描换根的方法,先DFS预处理,然后枚举每个点进行答案的更新。
具体来说,DFS的时候要预处理这样一个数组:size[i]代表当以1这个节点为树根时(换根),以i节点为根的子树的节点数。注意,这里的节点数实际上是子树的节点的权值和!和普通的树的重心不一样。同时,DFS时还能求出以1为树根时的距离和,存到f[1]里。
然后从1开始进行转移,设x的下一个节点是y,相较于x作为树根的路径和f[x],可以看到f[y]的值的变化情况:
1.减少了size[y]*1,因为以y为根的子树的所有节点原来要走到x,现在可以少走一条边,总的就是减少了size[y].
2.增加了(size[1]-size[y])*1,因为除去以y为根的子树的节点,还剩下size[1]-size[y]个节点(乘上权值),而这些节点需要多走一步。
转移方程就可以写出来了f[y]=f[x]+(size[1]-size[y])*1-size[y]*1; 满足无后效性,而且能知道dp的“阶段”就是以1为树根的树的当前深度。在更新的时候对答案取min更新即可。
#include <bits/stdc++.h>
#define N 105
using namespace std;
int n,m,head[N],ver[2*N],Next[2*N],tot=0;
int num[N],f[N],size[N],ans=0;//f[i]表示以i为根的总距离,size[i]表示以u为根的子树的大小 包括自己(对子树所有节点权值求和)。
void add(int x,int y)
{
    ver[++tot]=y,Next[tot]=head[x],head[x]=tot;
}
void dfs(int x,int d,int pre)
{
    int i;
    size[x]=num[x];
    for(i=head[x];i;i=Next[i])
    {
        int y=ver[i];
        if(y==pre)continue;
        dfs(y,d+1,x);
        size[x]+=size[y];
    }
    f[1]+=num[x]*(d-1);//逐层累加 注意是当前节点乘以深度减一累加上去 
}
void dp(int x,int pre)
{
    int i;
    for(i=head[x];i;i=Next[i])
    {
        int y=ver[i];
        if(y==pre)continue;
        f[y]=f[x]+(size[1]-size[y])*1-size[y]*1; //转移 
        dp(y,x);
    }
    ans=min(ans,f[x]);
}
int main()
{
    cin>>n;
    int i;
    for(i=1;i<=n;i++)
    {
        int w,u,v;
        scanf("%d%d%d",&w,&u,&v);
        num[i]=w;//节点的权值 
        if(u)add(i,u),add(u,i);//得存双向边 
        if(v)add(i,v),add(v,i);
    }
    dfs(1,1,0);
    ans=f[1];
    dp(1,0);
    cout<<ans;
    return 0;
}



猜你喜欢

转载自www.cnblogs.com/lipoicyclic/p/12708661.html
今日推荐