牛客每日一題4.3 Shortest Path 樹dp

首先這個數據範圍,看着就是O(N)或者O(Nlogn)的東西,考慮樹dp。
先貪心的考慮。一個點怎麼找到另一個點?肯定是他能接觸到的最短的那個點,如果與他最短的那個點被用過了呢?找其他第二近的點,以此類推。
對於一個點,是隻有當找不到人了纔會繼續走上去找其他人匹配,不然不管多大的權值都是會跟那個人走的。因爲對於一條權值很大的邊,邊連着的兩個點如果深度深的點在往下匹配不到人,就必須走上匹配,最少需要經過這條邊一次,那麼對於一個必須經過這個邊的點他肯定是與另一個無法匹配的點去結合,不管這個權值多大大你都必須走這條邊。
在這裏插入圖片描述
就像這個圖,你 2-3和2-4的邊是必須走,是永遠無法避開的。因爲節點3往下找不到點跟他匹配,這條998的邊必須走。

這裏樹dp要去考慮邊對答案的貢獻跟一般的考慮點的貢獻不太一樣,我們只需要算每條邊產生多少貢獻,也就是每條邊會有多少個點對需要用到他。
f[i]就是以i的子樹的所有邊產生的貢獻。

f[i] = sum(f[to]) + sum(v);

這個v是什麼?如果size【to】(以to爲根的子樹的孩子數量)是奇數,那麼必然存在一個點是沒有匹配的對象,需要往上走,那麼往上走就必然需要經過這條邊,則吧這條邊的貢獻加上。最後輸出f【1】就是所有邊產生的貢獻,也就是答案。

int n,son[max_],f[max_];
vector<pair<int, int> > xian[max_];
void dfs(int now, int fa) {
	son[now] = 1;
	for (auto pa : xian[now]) {
		int to = pa.first, v = pa.second;
		if (to == fa)continue;
		dfs(to, now);
		son[now] += son[to];
		f[now] += f[to];
		if (son[to] % 2)f[now] += v;
	}
}
signed main() {
	int T ;
	cin >> T;
	while (T--){
		n = read();
		for (int i = 0; i <= n; i++)xian[i].clear(), f[i] = son[i] = 0;
		for (int i = 2; i <= n; i++) {
			int a = read(), b = read(), c = read();
			xian[a].push_back(make_pair(b, c));
			xian[b].push_back(make_pair(a, c));
		}
		dfs(1, 0);
		cout << f[1] << endl;
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章