【图论三大集合】
【图论三大集合】
不常见
https://www.cnblogs.com/Ash-ly/p/5775934.html
最小支配集
例题
定义
对于树V
从V中取尽量少的点组成一个集合, 使得 V 中剩余的点都与取出来的点有边相连
贪心策略
(1)选择一点为树根
(2)深度优先遍历得到遍历序列
(3)所得序列的反向序列的顺序进行贪心:
一个节点既不属于支配集也不和支配集相连->将其父节点加入支配集
-> 支配集中点的个数加 1, 标记当前节点, 当前节点的父节点, 当前节点的父节点的父节点
代码
#include<bits/stdc++.h>
using namespace std;
const int N=15000;
int n;
//邻接表存图
int e[N],ne[N],h[N],idx;
bool has_father[N];
//求dfs序
int newpos[N];//dfs序:长度为n
int pre[N];//存储父节点
bool visit_[N];//dfs判重
int now=0;//记录dfs遍历几个点
//求最小支配集(MDS)
bool s[N];//是否覆盖
bool set_[N];//是否被取
int ans=0;
void add(int x,int y){
e[idx]=y;
ne[idx]=h[x];
h[x]=idx++;
}
void dfs(int x){
newpos[now++]=x;//记录dfs序
for(int i=h[x];i!=-1;i=ne[i]){
int j=e[i];
if(!visit_[j]){
visit_[j]=true;
pre[j]=x;//记录父节点
dfs(j);
}
}
}
void MDS(){
for(int i=n-1;i>=0;i--){//逆序贪心(注意now从0-(n-1))
int t=newpos[i];
if(!s[t]){//该点没被覆盖:将该点的父节点加入支配集
if(!set_[pre[t]]){
set_[pre[t]]=1;
ans++;
}
//覆盖三个点:当前节点 当前节点父节点 当前节点父节点的父节点
s[t]=1;
s[pre[t]]=1;
s[pre[pre[t]]]=1;
}
}
}
int main(){
scanf("%d",&n);
memset(h,-1,sizeof h);
for(int i=1;i<=n;i++){
int tmp;
scanf("%d",&tmp);
scanf("%lld",&k[tmp]);
int m;
scanf("%d",&m);
while(m--){
int son;
scanf("%d",&son);
add(tmp,son);
has_father[son]=1;
}
}
int root=1;
while(has_father[root]) root++;
//dfs序初始化
visit_[1]=true;
pre[1]=1;
dfs(root);
MDS();
printf("%d",ans);
return 0;
}