【BZOJ 3037】 创世纪 树形DP

来源:互联网 发布:软件需求工程项目 编辑:程序博客网 时间:2024/04/28 11:02

树的最小支配集:从v中取尽量少的点组成一个集合,使得对于v中剩余的点都与取出来的点有边相连。
怎么求?
如果是正常的树(无向)的话对于每个节点就是三种状态:
0.这个点被选
1.这个点被父亲节点覆盖
2.这个点被儿子节点覆盖
的最小支配集。
对于这道题呢,建反图后变成外向基环树林,所以对于状态就少了一维(没有状态2),还要在环上随便找一个点将环拆开,然后在枚举这个点选不选,若这个点为x,指向的点为y,则:
1.选x,则f[y][1]=0;
2.不选x,正常做最小支配集即可。
最后就是要注意有向图的找环阿 dfs阿和无向图的区别阿!!!

#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<vector>using namespace std;const int inf=0x3f3f3f3f;const int N=1000011;int fa[N],g[N],f[N],in[N];int head[N],next[N*2],key[N*2],tot,vis[N];int n,p;void add(int x,int y){    tot++;    next[tot]=head[x];    head[x]=tot;    key[tot]=y;}void DFS(int x)  {      vis[x]=1;      if(vis[fa[x]])          p=x;      else         DFS(fa[x]);  }  void dfs(int x,int fg,int root){   if(x==fg)g[x]=0,f[x]=1;   else g[x]=inf,f[x]=1;   vis[x]=1;   for(int i=head[x];i;i=next[i])   {     int y=key[i];     if(y!=root)     {      dfs(y,fg,root);      g[x]+=min(g[y],f[y]);      g[x]=min(g[x],f[x]+f[y]-1);//枚举有哪一个儿子选了 或哪些儿子选了       f[x]+=min(f[y],g[y]);     }   }}int main(){   cin>>n;   for(int i=1;i<=n;i++)   {      scanf("%d",&fa[i]);//建反向图       add(fa[i],i);   }   int ans=0;   for(int i=1;i<=n;i++)   if(vis[i]==0)   {      DFS(i);int x=p;      int y=fa[x];      if(x)      {         int tmp=inf;         dfs(x,y,x);//强行选x这个点          tmp=min(tmp,f[x]);         dfs(x,-1,x);//不选x         tmp=min(tmp,g[x]);         ans+=tmp;      }   }   printf("%d",n-ans);}
0 0
原创粉丝点击