Strategic Game
Time Limit: 10 Seconds
Memory Limit: 32768 KB
Bob enjoys playing computer games, especially strategic
games, but sometimes he
cannot find the solution fast enough and then he is very sad. Now he
has the
following problem. He must defend a medieval city, the roads of which
form a
tree. He has to put the minimum number of soldiers on the nodes so
that they
can observe all the edges. Can you help him?
Your program should find the minimum number of soldiers that Bob has
to put
for a given tree.
The input file contains several data sets in text format. Each data
set represents
a tree with the following description:
the number of nodes
the description of each node in the following format
node_identifier:(number_of_roads) node_identifier1 node_identifier2
... node_identifier
or
node_identifier:(0)
The node identifiers are integer numbers between 0 and n-1, for n
nodes (0 <
n <= 1500). Every edge appears only once in the input data.
For example for the tree:
the solution is one soldier ( at the node 1).
The output should be printed on the standard output. For each given
input data
set, print one integer number in a single line that gives the result
(the minimum
number of soldiers). An example is given in the following table:
Input
4
0:(1) 1
1:(2) 2 3
2:(0)
3:(0)
5
3:(3) 1 4 2
1:(1) 0
2:(0)
0:(0)
4:(0)
Output
1
2
題意:給出樹的結點之間的關系,如1:(2) 2 3
,表示2和3的父親都是1.在1點設點,則其鄰接點2和3都可被覆蓋,求出最少要多少個點才能覆蓋整棵樹。即最小頂點覆蓋。
分析:樹的各點之間只有一條邊,而且樹明顯的特性是有層次的數據結構。f[u][0]表示以u為根且u不加入覆蓋集的最小頂點覆蓋,f[u][1]表示以u為根且u加入覆蓋集的最小頂點覆蓋。每個點都有兩個狀態,就是加入與不加入覆蓋集。
最優子結構:要知道f[u][0]=只需知道u的子節點個數;要知道f[u][1],則要知道分別以u的孩子v們為根的最小頂點覆蓋,對這種情況,每個孩子可加入也可不加入覆蓋集(取最優的),以為樹有層次,所以要知道根部,就要知道孩子的最小頂點覆蓋,孩子的最小頂點覆蓋跟父親沒有關系。
子問題重疊:這里沒有子問題重疊。
邊界問題:每個節點對應兩種邊界問題,即當處理一個節點是,有取和不取兩種情況。
子問題獨立:對于u的孩子v1,v2:v1的孩子跟v2的孩子毫無關系。這種性質延伸到u的所有孩子上,即每個孩子的解互相沒有關系。
代碼:
#include<stdio.h>
#define Min(a, b) a < b ? a : b
#define maxn 1510
int up[maxn], f[maxn][2], n;
void dp(int u)
{
if (f[u][1] != -1)
{
return;
}
int i, v;
for (v = 0, f[u][1] = 1, f[u][0] = 0; v < n; v++)//對于每個點,遞歸到每個孩子解決問題后才能解決自身問題
{
if (up[v] == u)
{
dp(v);
f[u][1] += Min(f[v][0], f[v][1]);
f[u][0] += f[v][1];
}
}
}
int main()
{
int i, u, v, m, root;
while (scanf("%d", &n) != EOF)
{
for (i = 0; i < n; i++)
{
up[i] = -1;
f[i][0] = f[i][1] = -1;
}
for (i = 0; i < n; i++)
{
scanf("%d:(%d)", &u, &m);
while (m--)
{
scanf("%d", &v);
up[v] = u;
}
}
root = 0;
while (up[root] != -1)
{//查找根結點
root = up[root];
}
dp(root);
printf("%d\n", Min(f[root][1], f[root][0]));
}
return 0;
}