洛谷 P2986 [USACO10MAR]偉大的奶牛彙集(樹形動規)

題目描述

Bessie is planning the annual Great Cow Gathering for cows all across the country and, of course, she would like to choose the most convenient location for the gathering to take place.ios

Bessie正在計劃一年一度的奶牛大集會,來自全國各地的奶牛未來參加這一次集會。固然,她會選擇最方便的地點來舉辦此次集會。ide

Each cow lives in one of N (1 <= N <= 100,000) different barns (conveniently numbered 1..N) which are connected by N-1 roads in such a way that it is possible to get from any barn to any other barn via the roads. Road i connects barns A_i and B_i (1 <= A_i <= N; 1 <= B_i <= N) and has length L_i (1 <= L_i <= 1,000). The Great Cow Gathering can be held at any one of these N barns. Moreover, barn i has C_i (0 <= C_i <= 1,000) cows living in it.優化

每一個奶牛居住在 N(1<=N<=100,000) 個農場中的一個,這些農場由N-1條道路鏈接,而且從任意一個農場都可以到達另一個農場。道路i鏈接農場A_i和B_i(1 <= A_i <=N; 1 <= B_i <= N),長度爲L_i(1 <= L_i <= 1,000)。集會能夠在N個農場中的任意一個舉行。另外,每一個牛棚中居住者C_i(0 <= C_i <= 1,000)只奶牛。this

When choosing the barn in which to hold the Cow Gathering, Bessie wishes to maximize the convenience (which is to say minimize the inconvenience) of the chosen location. The inconvenience of choosing barn X for the gathering is the sum of the distances all of the cows need to travel to reach barn X (i.e., if the distance from barn i to barn X is 20, then the travel distance is C_i*20). Help Bessie choose the most convenient location for the Great Cow Gathering.spa

在選擇集會的地點的時候,Bessie但願最大化方便的程度(也就是最小化不方便程度)。好比選擇第X個農場做爲集會地點,它的不方便程度是其它牛棚中每隻奶牛去參加集會所走的路程之和,(好比,農場i到達農場X的距離是20,那麼總路程就是C_i*20)。幫助Bessie找出最方便的地點來舉行大集會。blog

Consider a country with five barns with [various capacities] connected by various roads of varying lengths. In this set of barns, neither barn 3 nor barn 4 houses any cows.three

1 3 4 5圖片

@--1--@--3--@--3--@[2]ci

[1] |get

2 | @[1] 2 Bessie can hold the Gathering in any of five barns; here is the table of inconveniences calculated for each possible location:

Gather ----- Inconvenience ------

Location B1 B2 B3 B4 B5 Total

1 0 3 0 0 14 17

2 3 0 0 0 16 19

3 1 2 0 0 12 15

4 4 5 0 0 6 15

5 7 8 0 0 0 15

If Bessie holds the gathering in barn 1, then the inconveniences from each barn are:

Barn 1 0 -- no travel time there!

Barn 2 3 -- total travel distance is 2+1=3 x 1 cow = 3 Barn 3 0 -- no cows there!

Barn 4 0 -- no cows there!

Barn 5 14 -- total travel distance is 3+3+1=7 x 2 cows = 14 So the total inconvenience is 17.

The best possible convenience is 15, achievable at by holding the Gathering at barns 3, 4, or 5.

輸入輸出格式

輸入格式:

 

  • Line 1: A single integer: N

  • Lines 2..N+1: Line i+1 contains a single integer: C_i

  • Lines N+2..2*N: Line i+N+1 contains three integers: A_i, B_i, and L_i

第一行:一個整數 N 。

第二到 N+1 行:第 i+1 行有一個整數 C_i

第 N+2 行到 2*N 行:第 i+N+1 行爲 3 個整數:A_i,B_i 和 L_i。

 

輸出格式:

 

  • Line 1: The minimum inconvenience possible

第一行:一個值,表示最小的不方便值。

 

輸入輸出樣例

輸入樣例#1: 複製
5 1 1 0 0 2 1 3 1 2 3 2 3 4 3 4 5 3 
輸出樣例#1: 複製
15 

 

如下複製yybyyb大佬的題解。。。(這道題能夠當模板題值得一記)

 

考慮若是依次枚舉每個點做爲集會的地點

使用DFS進行計算

而後再依次比較

時間複雜度O(n^2)

可是n的範圍太大,顯然會超時。

那麼,咱們應當如何優化?

先看看樣例

這裏寫圖片描述

經過一次O(n)的計算,很容易得出來

若是選擇1號節點,答案就是17

既然O(n^2)的計算沒法在時間內求解

那麼是否能夠遞推出來呢?

顯然是能夠的。

觀察若是已經知道1號節點所需的時間

那麼,咱們能夠作以下假設:

① 全部的牛首先到達了1號節點

② 3號節點以及他子樹上的節點都須要退回1->3的路徑的長度

③ 除了3號節點以及他子樹上的節點都須要前進1->3的路徑的長度

經過上面的三條東西,咱們就能夠從任意一個父節點推出子節點的時間

因此,又是一遍O(n)的計算就能夠推出最終的答案

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define MAX 200100
#define ll long long
inline ll read()
{
      register ll x=0,t=1;
      register char ch=getchar();
      while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
      if(ch=='-'){t=-1;ch=getchar();}
      while(ch<='9'&&ch>='0'){x=x*10+ch-48;ch=getchar();}
      return x*t;
}

ll dis[MAX],C[MAX],Q[MAX],f[MAX],Sum,Ans=1000000000000000000;

struct Line
{
      ll v,next,w;
}e[MAX];

ll h[MAX],cnt=1,N;

inline void Add(ll u,ll v,ll w)
{
      e[cnt]=(Line){v,h[u],w};
      h[u]=cnt++;
}
//使用兩遍DFS
//第一遍以任意點爲根節點計算一遍
//dis[i]表示以i爲根的子樹到根的距離之和 
ll DFS(ll u,ll ff)
{
      ll tot=0;
      for(ll i=h[u];i;i=e[i].next)
      {
               ll v=e[i].v;
               if(v!=ff)
               {
                      ll s=DFS(v,u);//子樹上牛的數量 
                      dis[u]+=dis[v]+e[i].w*s;//統計 
                   tot+=s;//牛的個數
               }
      }
      return Q[u]=tot+C[u];
}
//第二遍計算偏移後的值
//先能夠假設走到當前節點的父節點
//再讓當前本身點全部牛退回來,父節點的全部牛走過去便可 
void DFS2(ll u,ll ff)
{
       for(ll i=h[u];i;i=e[i].next)
       {
                  ll v=e[i].v;
                  if(v!=ff)
                  {
                           ll ss=e[i].w;
                           f[v]=f[u]-Q[v]*ss+(Sum-Q[v])*ss;
                           DFS2(v,u);
                  }
       }
}

int main()
{
      N=read();
      for(ll i=1;i<=N;++i)
        C[i]=read();
      for(ll i=1;i<=N;++i)
        Sum+=C[i];//統計牛的總數 
      for(ll i=1;i<N;++i)
      {
                 ll u=read(),v=read(),w=read();
                 Add(u,v,w);
                 Add(v,u,w);
      }

      DFS(1,1);//求出以1爲彙集處的結果 

      DFS2(1,1);//求出其餘的偏移值

      for(ll i=1;i<=N;++i)
            Ans=min(Ans,f[i]);

        cout<<Ans+dis[1]<<endl;

        return 0;
}

  

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
#define man 200100
#define ll long long
ll n,m,a[man],ans=123456480,sum=0;
ll son[man],dis[man<<1];
struct edge
{
    ll next,to,dis;
    }e[man];
ll num=0,head[man<<2];
ll f[man];
inline void add(ll from,ll to,ll dis)
{
    e[++num].next=head[from];
    e[num].to=to;
    e[num].dis=dis;
    head[from]=num;
    }
ll dfs(ll u,ll father)
{
    ll tot=0;
    for(ll i=head[u];i;i=e[i].next)
    {
        ll to=e[i].to;
        if(to==father)continue;
        ll d=dfs(to,u);
        dis[u]+=dis[to]+e[i].dis*d;
        tot+=d;
        }
    return son[u]=tot+a[u];
    }
void sch(ll u,ll father)
{
    for(ll i=head[u];i;i=e[i].next)
    {
        ll to=e[i].to;
        if(to==father)continue;
        f[to]=f[u]-son[to]*e[i].dis+(sum-son[to])*e[i].dis;
        sch(to,u);
        }
    }
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++)
    {	cin>>a[i];sum+=a[i];}
    for(int i=1,x,y,d;i<n;i++)
    {
        cin>>x>>y>>d;
        add(x,y,d);add(y,x,d);
        }
    memset(f,0,sizeof(f));
    dfs(1,1);
    sch(1,1);
    //cout<<f[1]<<endl;
    ans=f[1];
    for(int i=1;i<=n;i++)
    {
        ans=min(ans,f[i]);
        }
    cout<<ans+dis[1]<<endl;
    return 0;
    }
相關文章
相關標籤/搜索