奶牛大集合(USACO2010_gather)

本文介绍了一种在树形结构中计算每个节点与其子节点间距离之和的算法。通过定义up[i]和down[i]来分别记录不属于i节点子树的其他节点到i节点的距离总和以及i节点子树上所有节点到i节点的距离总和。利用DFS两次遍历实现计算,并最终找到最小距离和的节点。
算法:搜索

一开始把树结构看成图了,结果只过了一个点,仔细一看,才能发现这确实是一个考察树的题目(n个点n-1条边),题目中我们需要确定的就是子结点与父结点的关系。
首先,我们用up[i]表示不属于i的子树上的点的其它的所有点到i的距离和,用down[i]表示属于i的子树上的所有点到i的距离和。用sum[i]表示i的子结点的所有点的牛的数目。
down[dad]=down[i]+sum[i]*dis[i](这个比较好想)
up[i]=up[dad]+down[dad]-down[i]-sum[i]*dis[i]+(sumcow-sum[i])*dis[i](这个就比较难想了)

用双向边建图,然后搜索两次,最后枚举就得出答案了……

program gather;

const
 maxn=100000;

type
 atp=record
  y,next,dis:int64;
 end;

var
 n,tot:int64;
 sumcow,ans:int64;
 a,first:array [0..maxn] of int64;
 sum,up,down:array [0..maxn] of int64;
 map:array [0..maxn*2] of atp;

procedure init;
var
 i,x,y:longint;
 dis:int64;
begin
 ans:=2147483647000;
 readln(n);
 for i:=1 to n do
  begin
   readln(a[i]);
   inc(sumcow,a[i]);
  end;
 for i:=1 to n-1 do
  begin
   readln(x,y,dis);
   inc(tot);
   map[tot].y:=y;
   map[tot].dis:=dis;
   map[tot].next:=first[x];
   first[x]:=tot;
   inc(tot);
   map[tot].y:=x;
   map[tot].dis:=dis;
   map[tot].next:=first[y];
   first[y]:=tot;
  end;
end;

procedure dfs1(x,bef:longint);
var
 t:longint;
begin
 t:=first[x];
 sum[x]:=a[x];
 while t>0 do
  begin
   if map[t].y<>bef then
    begin
     dfs1(map[t].y,x);
     inc(sum[x],sum[map[t].y]);
     inc(down[x],sum[map[t].y]*map[t].dis);
     inc(down[x],down[map[t].y]);
    end;
   t:=map[t].next;
  end;
end;

procedure dfs2(x,bef:longint);
var
 t:longint;
begin
 t:=first[x];
 while t>0 do
  begin
   if map[t].y<>bef then
    begin
     up[map[t].y]:=up[x]+down[x]-down[map[t].y]-sum[map[t].y]*map[t].dis+(sumcow-sum[map[t].y])*map[t].dis;
     dfs2(map[t].y,x);
    end;
   t:=map[t].next;
  end;
end;

procedure main;
var
 i:longint;
begin
 dfs1(1,0);
 dfs2(1,0);
 for i:=1 to n do if up[i]+down[i]<ans then ans:=up[i]+down[i];
end;

begin
 assign(input,'gather.in'); reset(input);
 assign(output,'gather.out'); rewrite(output);

 init;
 main;
 writeln(ans);

 close(input); close(output);
end.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值