樹網的核(NOIP2007提高組)

2021-09-25 03:46:54 字數 3606 閱讀 2369

傳送門

這道題的題意首先要讀懂。

簡單說,題目是想要我們選取乙個樹核,使得樹核外的其他點到樹核的距離的最大值最小。

我們存圖採用鄰接矩陣和鍊錶兼用法,鄰接矩陣用於跑floyd,鍊錶用於遍歷與某個點直接相連的點。

我們首先用floyd處理出各個點之間的距離,備用。

接下來我們先找出直徑的長度,這個很好解決,直接列舉每兩個點之間的距離,最大的乙個就是。

題目說我們的樹核一定是在直徑上,所以我們應該找出直徑再進行下一步。

這個應該比較容易,我們先列舉找出直徑的端點,然後dfs一下即可。

但是請注意,我們只需要乙個直徑即可,不需要每個直徑都進行一邊找樹核

這個我單獨放在後面證明。

所以我們找到乙個直徑後就可以break掉了。

那麼當我們把直徑存在陣列裡以後,接下來就是找樹核了。

怎麼辦?列舉好了。

那麼應該按什麼順序去列舉呢?

因為我們的直徑一定是一條鏈(這個應該不要證明),而樹核一定是連續的,所以我們可以用直徑陣列的下表來表示,即l和r。

我們此處還要用到乙個結論:

乙個樹核一定不會比它的子樹核更差,即乙個樹核的偏心距一定小於等於其子樹核的偏心距

這個我們也放到後面證明。

所以這提示我們,我們列舉樹核時,計算過乙個樹核後一定不能計算它的子樹核(即被其包含的樹核)。所以我們的移動策略就出來了:

如果我們加入樹核右邊的乙個新點時,樹核長度小於等於s,那麼r++

如果不行,那麼我們就丟棄左邊的點,即l++,直到可以囊括右邊的新點,那麼r++

如果減到只剩乙個點時,仍然不能囊括新的點,說明這條邊的長度大於s,那麼我們就直接加入新的點,把舊點全部丟掉,即l++,r++

這樣就可以了。我們可以發現按此順序執行後r一定會增加的,也就是說變換後的樹核一定會加入乙個新的點,也就絕對和原來的樹核不一樣,而同時又保證了,直徑上每個點都有機會加入樹核,所以就成功地列舉完了所有的樹核。

那麼既然已經可以列舉樹核了,接下來就是計算每乙個樹核的偏心距了。

題目說是樹核外每乙個點到樹核距離的最大值,我們可以轉換一下,從樹核上各個點到其他點的距離的最大值。

但是這樣的說法是有問題的,還有乙個要求,即樹核上的某個點到樹核外這個點的路徑不能和樹核有重疊,那麼怎麼保證這一點呢?

這就要用到我們的鍊錶了。

我們先列舉樹核上的點,對於每個點(設為cur),我們列舉與其相連的點,然後找出非樹核內部的點,設為u,然後再列舉所有的點(設為k),滿足如下的式子即可說明k與cur的路徑不與樹核重疊:

[u][k]

[k];

應該很顯然,如果需要證明,我放在後面。

這樣問題就迎刃而解了。

下面我們來證明一下前面說的幾個結論:

直徑不一定是唯一的,但可以證明:各直徑的中點(不一定恰好是某個結點,可能在某條邊的內部)是唯一的,我們稱該點為樹網的中心。

那麼我們現在分情況討論。

首先,中心在節點。

如圖,數字只是邊的標號,不是長度。長度可以由圖直接看出。而且,這裡只畫出了最兩端的端點,也就是說我連線的線上還有其他的點未標出,如1、2、3的交點。

這裡有六條直徑,分別是134、135、136、45、46、56

紅點是中心。

那麼我們來看其他支鏈與直徑的關係,我們可以很容易地知道,中心上的其他支鏈,對於每條直徑都是等價的,所以不必考慮每一條直徑。

再看直徑上其他結點的小支鏈,如2

若2的長度大於1,那麼直徑就不再是135,違背假設,所以2的長度小於等於1.

對於與2相交的直徑,如135,其上的任意一點到2的端點的距離,均小於到4或6的端點,因此並不影響偏心距(因為偏心距是最大值)。

對於與2不相交的直徑,如46,那麼就更顯然了,其上每一點到2端點的距離均小於到1端點的距離,同樣不影響。

而對於直徑本身的支鏈,由對稱性可知影響是一樣的。

綜上,所有的直徑對於本圖是等價的。

那麼對於中心在邊上的也是類似的證明思路,自己證明試試看吧!

我們直接上圖:

這裡我們取123作為樹核,那麼其子樹核之一為23。

對於未變動的2和3,其連線的4,5,7到其的距離均未變。

而對與1這一邊,我們加上乙個1後,6到樹核的距離變短了。

也就是說樹核長度增加後,其他點到樹核的距離,要麼沒變,要麼減小了,可見偏心距也是要麼沒變,要麼減小。

所以乙個樹核一定不會比子樹核差。

挺拗口的,反正你也知道我要證明前面哪句話。

這是較為明顯的。

若該路徑與樹核還有另一交點v,即cur和v之間有兩條不同路徑,一條是我們選取的路徑,另一條是通過樹核的路徑(一定是不同的,只要注意兩條路徑與cur的連線點即可),那麼我們就形成了乙個環,然而這是乙個樹,違背題意。

所以,最後就形成了我們的**:

#include

#include

#include

#include

using

namespace std;

const

int maxn=

305;

vector<

int> g[maxn]

;//鍊錶

int n,s;

[maxn]

;//鄰接矩陣

int dm[maxn]

;//儲存直徑路徑

int vis[maxn]

;//是否存在於當前選定的和核

int dia;

//直徑長度

int ans;

//最終答案

int num;

//直徑所含節點數

intcalc

(int l,

int r)

for(

int k=

1;k<=n;k++)}

}}return curans;

}void

dfs(

int cur,

int tar)

for(

int i=

0;i.size()

;i++)}

}void

work

(int u,

int v)

else}if

(l==r)

} ans=

min(ans,

calc

(l,r));

}}intmain()

for(

int k=

1;k<=n;k++)}

}}ans=

0x3f3f3f3f

; dia=0;

for(

int i=

1;i<=n;i++)}

}for

(int i=

1;i<=n;i++)}

}printf

("%d"

,ans)

;return0;

}

NOIP2007提高組 樹網的核

noip2007提高組試題4。設 t v,e,w 是乙個無圈且連通的無向圖 也稱無根樹 每條邊帶有正整數的權,我們稱 t 為樹網 treenetwork 其中 v,e 分別表示結點與邊的集合,w 表示各邊長度的集合,並設 t 有 n 個結點。路徑 樹網中任何兩個結點 a,b 都存在唯一的一條簡單路徑...

NOIP2007 樹網的核

參考了某位神牛的題解之後才發現原來這道題並不是很難 還是自己太弱了 原來的想法本來是快排找直徑,通過dfs確定在直徑中的點,然後再分別列舉每兩個點形成的邊,以邊的兩點做floyd通過打擂台確定偏心距。自己的想法真的很麻煩而且就算編出來少說也要200 行的 這在比賽中是不可能也是做不到的。正規且高效的...

NOIp 2007 樹網的核

問題描述 設 t v,e,w 是乙個無圈且連通的無向圖 也稱為無根樹 每條邊帶有正整數的權,我 們稱t 為樹網 treenetwork 其中v,e分別表示結點與邊的集合,w 表示各邊長度的集合,並設t 有n個結點。路徑 樹網中任何兩結點a,b 都存在唯一的一條簡單路徑,用d a,b 表示以a,b 為...