CellPhone Network

Time Limit: 1000MS

Memory Limit: 65536K

Total Submissions: 7165

Accepted: 2558

Description

Farmer John has decided to give each of his cows a cell phone inhopes to encourage their social interaction. This, however, requires him to setup cell phone towers on his N (1 ≤ N ≤ 10,000) pastures (convenientlynumbered 1..N) so they can all communicate.

Exactly N-1pairs of pastures are adjacent, and for any two pastures A and B (1 ≤ A ≤ N;1 ≤ B ≤ NA ≠ B)there is a sequence of adjacent pastures such that isthe first pasture in the sequence and B is the last. Farmer John can onlyplace cell phone towers in the pastures, and each tower has enough range toprovide service to the pasture it is on and all pastures adjacent to thepasture with the cell tower.

Help him determine the minimum number of towers he must install toprovide cell phone service to each pasture.

Input

* Line 1: A single integer: N
* Lines 2..N: Each line specifies a pair of adjacent pastures with twospace-separated integers: A and B

Output

* Line 1: A single integer indicating the minimum number of towersto install

SampleInput

5

1 3

5 2

4 3

3 5

SampleOutput

2

Source

​USACO 2008 January Gold​

算法分析:

题意:在一棵树上选出最小的点集,使得每个点都满足自己在集合中或相连的点在集合中 。

分析:

贪心树的最小支配集思路(​​点这里​​)

另一种做法:​​树形DP​​。

第一步:以1号点dfs整棵树,求出每个点在DFS中的编号i和每个点的父亲节点编号f[i]。

第二步:按DFS的反向序列检查,如果当前点u既不属于支配集也不与支配集中的点相连,且它的父亲f[u]也不属于支配集,将其父亲点f[u]加入支配集,支配集个数ans加1。标记当前结点u、当前结点的父节点f[u](属于支配集)、当前结点的父节点的父节点f[f[u]](与支配点中的点相连)。

代码实现:

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstdlib>
#include<queue>
#include<vector>
const long long INF = 0x3f3f3f3f3f3f3f3f;
#define PI acos(-1.0)
#define N 20025
#define MOD 2520
#define E 1e-12
using namespace std;
int n,a[N],b[N],f[N],vis[N],flag[N],cnt;
/*
a[i]表示bfs判重
b[i]表示现在bfs中第i个点是哪个点(对应哪一个序号)
cnt表示现在bfs中点的个数
f[i]表示第i个点的父亲编号

vis[i]表示i点是否被覆盖
flag[i]表示i点是否属于要求大的点集
*/
vector<int>g[N];
int dfs(int u)//dfs求出每一个点的父亲节点
{
cnt++;
a[u]=cnt;
b[cnt]=u;

for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(a[v]==-1)
{
f[v]=u;
dfs(v);
}
}
return 0;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
for(int i=1;i<=n;i++)
g[i].clear();
memset(a,-1,sizeof(a));
memset(vis,0,sizeof(vis));
memset(flag,0,sizeof(flag));
memset(f,0,sizeof(f));

for(int i=1;i<n;i++)
{

int x,y;
scanf("%d%d",&x,&y);
g[x].push_back(y);
g[y].push_back(x);
}
cnt=0;
dfs(1);
int ans=0;
//贪心求最小支配集
for(int i=n;i>=1;i--)//方向序列检查
{
int u=b[i];

if(vis[u]==0)//当前点未被覆盖,表示当前点既不属于支配集,也不与支配集的点相连
{
if(flag[f[u]]==0)//当前点的父亲不属于支配集
{
flag[f[u]]=1;//加入支配集
ans++; //支配集个数加1
}
//标记当前点、当前结点的父节点、当前结点的父节点的父节点
vis[u]=1;
vis[f[u]]=1;
vis[f[f[u]]]=1;
}
}
printf("%d\n",ans);
}
return 0;
}