题意:每个牧场的电塔可以覆盖与该牧场相邻的电塔,为了让所有牛都可以打电话,求建的电塔的最小数量
思路:即求一棵树的最小支配集(最少的能够覆盖所有点的点数)
贪心策略:先对树做一遍dfs,之后根据dfs的逆序对节点进行标记,s[i]表示该点已被覆盖,set[i]表示该点在最小支配集里,首先对子节点判断,若未被覆盖,则判断其父节点是否在支配集里,若不在,则将其放入支配集,覆盖该点、该点父节点、父节点的父节点。由于是根据逆序来判断,故在处理到每个点之前其子节点必定已经处理完毕,所以可以证明其正确性。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;
const int maxn = 1e5+5;
int pre[maxn], vis[maxn], pos[maxn], s[maxn], set[maxn], now, n, m;
vector<int> g[maxn];
void dfs(int x)
{
pos[now++] = x;
for (int i = 0; i < g[x].size(); i++) {
if (!vis[g[x][i]]) {
vis[g[x][i]] = 1;
pre[g[x][i]] = x;
dfs(g[x][i]);
}
}
}
int solve()
{
memset(s, 0, sizeof(s));
memset(set, 0, sizeof(set));
int ans = 0;
for (int i = n-1; i >= 0; i--) {
int t = pos[i];
if (!s[t]) {
if (!set[pre[t]]) {
set[pre[t]] = 1;
ans++;
}
s[t] = s[pre[t]] = s[pre[pre[t]]] = 1;
}
}
return ans;
}
int main()
{
// freopen("test.txt", "r", stdin);
while (cin >> n) {
memset(vis, 0, sizeof(vis));
now = 0;
vis[1] = 1;
pre[1] = 1;
for (int i = 0; i < n-1; i++) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1);
cout << solve() << endl;
}
return 0;
}