UVa 1218 - Perfect Service(树形DP)
链接:
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3659
题意:
有n(n≤10000)台机器形成树状结构。要求在其中一些机器上安装服务器,
使得每台不是服务器的计算机恰好和一台服务器计算机相邻。求服务器的最少数量。
分析:
按照每个结点的情况进行分类:
一、d(u,0):u是服务器,则每个子结点可以是服务器也可以不是。
二、d(u,1):u不是服务器,但u的父亲是服务器,这意味着u的所有子结点都不是服务器。
三、d(u,2):u和u的父亲都不是服务器。这意味着u恰好有一个儿子是服务器。
状态转移方程如下(其中v是u的子结点):
d(u,0) = sum{min(d(v,0), d(v,1))} + 1。
d(u,1) = sum(d(v,2))。
d(u,2)需要枚举当服务器的子结点编号v,然后把其他所有子结点v'的d(v',2)加起来,再和d(v,0)相加。
可以利用已经算出的d(u,1)写出一个新的状态转移方程:d(u,2) = min(d(u,1) – d(v,2) + d(v,0))。
最终答案为min(d(1,0), d(1,2))。
代码:
1 #include <cstdio> 2 #include <vector> 3 using namespace std; 4 5 const int UP = 10000 + 5; 6 int F[UP], d[UP][3]; // F[r]为结点r的父结点 7 vector<int> seq, edge[UP]; // seq为结点访问顺序序列 8 9 void dfs(int r, int f){ 10 F[r] = f; 11 seq.push_back(r); 12 for(int i = 0; i < edge[r].size(); i++){ 13 int b = edge[r][i]; 14 if(b != f) dfs(b, r); 15 } 16 } 17 18 int main(){ 19 int n; 20 while(scanf("%d", &n) == 1){ 21 seq.clear(); 22 for(int i = 1; i <= n; i++) edge[i].clear(); 23 for(int f, b, i = 1; i < n; i++){ 24 scanf("%d%d", &f, &b); 25 edge[f].push_back(b); 26 edge[b].push_back(f); 27 } 28 29 dfs(1, -1); // 以结点1为根建立有根树 30 for(int p = seq.size() - 1; p >= 0; p--){ 31 int r = seq[p]; 32 d[r][0] = 1; d[r][1] = 0; 33 for(int i = 0; i < edge[r].size(); i++){ 34 int b = edge[r][i]; 35 if(b == F[r]) continue; 36 d[r][0] += min(d[b][0], d[b][1]); // d[r][0]代表r是服务器 37 d[r][1] += d[b][2]; // d[r][1]代表r不是服务器,但其父结点是 38 } 39 d[r][2] = 12345; //d[r][2]代表r及其父结点都不是服务器 40 for(int i = 0; i < edge[r].size(); i++){ 41 int b = edge[r][i]; 42 if(b == F[r]) continue; 43 d[r][2] = min(d[r][2], d[r][1] - d[b][2] + d[b][0]); 44 } 45 } 46 printf("%d\n", min(d[1][0], d[1][2])); 47 scanf("%d", &n); // 读取结束标记 48 } 49 return 0; 50 }