基本概念:
树的支配集:对于图G = (V, E), 若点集V‘是图的一个支配集,则对于图中任意一个顶点u , 要么u属于V’,要么与V‘中顶点相邻。
而读懂题意后,这个题就是一个裸的树的最小支配集了,很多人都是用的树形dp,弱菜表示不会。。。于是。。。只好写个贪心的代码A了。。。
求树的最小支配集的贪心解法是: 选择一个点,求出dfs序列,按照得到的序列的反向序列进行贪心,对于一个既不属于支配集也不与支配集中点相连的点来说,如果他的父节点不属于支配集,将其父节点加入支配集。
同样对于树的最小点覆盖和最大独立集都能用贪心做,程序上也只需将greedy()函数修改一下。
#include<iostream>
#include<algorithm>
#include<vector>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 11111;
int n, u, v, dfs_clock;
int pos[maxn], p[maxn];
bool vis[maxn];
vector<int> G[maxn];
void dfs(int u, int fa) //求树的dfs序列 保存在pos数组中
{
pos[dfs_clock++] = u;
for(int i=0; i<G[u].size(); i++)
{
int v = G[u][i];
if(!vis[v] && v != fa)
{
vis[v] = 1;
p[v] = u;
dfs(v, u);
}
}
}
int greedy() //贪心求树的最小支配集
{
int ret = 0;
bool s[maxn] = {0};
bool set[maxn] = {0};
for(int i=n-1; i>=0; i--)
{
int t = pos[i];
if(!s[t])
{
if(!set[p[t]])
{
set[p[t]] = 1;
ret++;
}
s[t] = 1;
s[p[t]] = 1;
s[p[p[t]]] = 1;
}
}
return ret;
}
int main()
{
while(~scanf("%d", &n))
{
for(int i=0; i<=n; i++) G[i].clear();
for(int i=1; i<n; i++)
{
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs_clock = 0;
memset(vis, 0, sizeof(vis));
dfs(1, -1);
printf("%d\n", greedy());
}
}
/*
附:
int greedy() //贪心求树的最小覆盖集
{
bool s[maxn] = {0};
bool set[maxn] = {0};
int ret = 0;
for(int i=n-1; i>=0; i--)
{
int t = pos[i];
if(!s[t] && !s[p[t]])
{
set[p[t]] = 1;
ret++;
s[t] = 1;
s[p[t]] = 1;
}
}
return ret;
}
int greedy() //贪心求树的最大独立集
{
bool s[maxn] = {0};
bool set[maxn] = {0};
int ret = 0;
for(int i=n-1; i>=0; i--)
{
int t = pos[i];
if(!s[t])
{
set[t] = 1;
ret++;
s[t] = 1;
s[p[t]] = 1;
}
}
return ret;
}
*/