1. 程式人生 > 實用技巧 >UVA1218 Perfect Service

UVA1218 Perfect Service

紫書上的樹形DP,挺香的。

\(\operatorname{Description}\)

給定一個 \(n\) 臺計算機形成的樹狀結構。要求在其中一些計算機上安裝伺服器,使得每臺不是伺服器的計算機恰好和一臺伺服器計算機相鄰。求伺服器的最小數量。

\(\operatorname{Solution}\)

考慮維護一個樹狀結構,設 \(1\) 號點為根節點,雙向建邊。

考慮樹形DP,設 \(f_u\) 為以 \(u\) 為根的子樹。

  • \(f_{u,1}\)\(u\) 是伺服器,則每個子節點可以是伺服器也可以不是。

  • \(f_{u,2}\)\(u\) 不是伺服器,但 \(u\) 的父親是伺服器,這意味著 \(u\)

    的所有子節點都不是伺服器。

  • \(f_{u,3}\)\(u\)\(u\) 的父親都不是伺服器,這意味著 \(u\) 恰好有一個兒子是伺服器。

然後我們考慮如何轉移。

為了方便闡述,令 \(v\)\(u\) 的子節點,定義集合 \(S\)\(u\) 的所有子節點的集合。

首先可以寫出:

\[f_{u,0}=\sum_{v∈S}\min(f_{v,0},f_{v,1})+1 \]

\[f_{u,1}=\sum_{v∈S}f_{v,2} \]

\(f_{v,2}\) 的轉移稍微複雜一點,考慮遍歷 \(v\),則當前 \(v\)\(u\) 子節點的唯一伺服器,設其他所有子節點為 \(v'\)

。得到:

\[f_{u,2}=\min(\sum_{v∈S}(f_{v,0}+\sum_{v'∈S-v}f_{v',2})) \]

由於 \(f_{u,2}\) 的轉移是 \(O(n^2)\) 的,故考慮優化。

看到上文中的 \(f_{u,1}=\sum_{v∈S}f_{v,2}\),所以對柿子進行處理後:

\[f_{u,2}=\min(\sum_{v∈S}(f_{v,0}+f_{u,1}-f{v,2})) \]

考慮最後的答案,由於根結點並沒有父親節點,故為 \(\min(f_{1,0},f_{1,2})\)

\(\operatorname{Code}\)

#include<bits/stdc++.h>
using namespace std;
const int N=10005;
const int Inf=10005;
int n,f[N][3];
vector <int> q[N];
void dfs(int u, int fa) {
	f[u][0]=1; f[u][1]=0; f[u][2]=Inf;
	for(int i=0;i<q[u].size();i++) {
		if(q[u][i]==fa) continue;
		int v=q[u][i]; dfs(v,u);
		f[u][0] += min (f[v][1],f[v][0]);
		f[u][1] += f[v][2];
		f[u][2] = min (f[u][2],f[u][1]-f[v][2]+f[v][0]);
	}
}
void Clear() {for(int i=1;i<=n;i++) q[i].clear();}
int main() {
    while (cin >> n) {
    	Clear();
    	for(int i=1;i<n;i++) {
    		int x,y; cin >> x >> y;
    		q[x].push_back(y); q[y].push_back(x);
		}
		dfs(1,0);
    	printf("%d\n",min (f[1][0],f[1][2]));
    	int d; cin >> d; if(d==-1) break;
	}
	return 0;
}