【帶權中位數】醫院的設置

题目描述

设有一棵二叉树,其中圈中的数字表示结点居民的人口,圈边上的数字表示结点的编号。现在要求在某个结点上建立一个医院,使所有居民所走的路径之和为最小,同时约定,相邻接点之间的距离为1,就本图而言,若医院建在1处,则距离和=4+12+2*20+2*40=136,若医院建在3处,则距离和=4*2+13+20+40=81,…
输入格式

其中第一行一个整数n,表示树的结点数(n<=100)。接下来的N行每行描述了一个结点的状况,包括三个整数,整数之间用空格(一个或多个)分隔,其中:第一个数为居民人口数;第二个数为左链接,为0表示无链接;第三个数为右链接。
输出格式

只有一个整数,表示最小距离和。
样例输入
5 13 2 3 4 0 0 12 4 5 20 0 0 40 0 0
样例输出
81
此題考察帶權中位數在二叉樹中的應用。
線性帶權中位數:

有一系列數據(排序後)a1, a2, a3, ..., an,它們每都有一個權值w1, w2, w3, ..., wn,則這一系列數據中位數為:到所有數的帶權距離和最小的數值。(當然和數學中的帶權中位數不一樣,這裡的中位數必須等於某一個數ai。)

並且容易知道,ai到比其小的所有帶權距離和與到比其大的所有帶權距離和的差值最小,也即ai到a1, a2, ..., a(i-1)的帶權距離和剛好大於ai到所有點帶權距離和的一半。

在樹上的帶權中位數即為到所有點帶權距離和剛好大於到所有點帶權距離和的一半,而把線性帶權中位數中的左右枚舉轉變為這裡的枚舉各個子樹,代價分析即分析從根節點到每一個子樹的代價變化。
ACCode:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <bitset>

using std::bitset;

const char fi[] = "rqnoj319.in";
const char fo[] = "rqnoj319.out";
const int maxN = 110;
const int MAX = 0x3fffff00;
const int MIN = -MAX;

bitset <maxN> tmp;
int g[maxN];
int d[maxN];
int lc[maxN], rc[maxN];
int n, root, L, R, ans, sum;

  void init_file()
  {
    freopen(fi, "r", stdin);
    freopen(fo, "w", stdout);
  }
    
  void readdata()
  {
    scanf("%d", &n);
    for (int i = 1; i < n + 1; ++i)
    {
      scanf("%d%d%d", g + i, lc + i, rc + i);
      sum += g[i];
      if (lc[i]) tmp.set(lc[i]);
      if (rc[i]) tmp.set(rc[i]);
    }
    for (int i = 1; i < n + 1; ++i)
      if (!tmp.test(i)) {root = i; break; }
  }
  
  void Dfs1(int i, int deep)
  {
    ans += deep * g[i];
    if (lc[i]) Dfs1(lc[i], deep + 1);
    if (rc[i]) Dfs1(rc[i], deep + 1);
    g[i] += g[lc[i]] + g[rc[i]];
  }
  
  void Dfs2(int i)
  {
    if ((g[lc[i]] << 1) > sum)
    {
      ans -= (g[lc[i]] << 1) - sum;
      if (lc[i]) Dfs2(lc[i]);
    }
    if ((g[rc[i]] << 1) > sum)
    {
      ans -= (g[rc[i]] << 1) - sum;
      if (rc[i]) Dfs2(rc[i]);
    }
  }
  
  void work()
  {
    Dfs1(root, 0);
    Dfs2(root);
    printf("%d", ans);
  }
  
int main()
{
  init_file();
  readdata();
  work();
  exit(0);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值