BZOJ3037: 创世纪
来源:互联网 发布:淘宝赚钱助手怎么用 编辑:程序博客网 时间:2024/04/28 20:55
题目大意:给定一个有向图,每个点出度为1,让你将这些点分为两个集合x和y,使得对于每一个y集合内的点都存在一个x集合内的点指向它,并且使得y集合尽可能大。
原图给的是一些内向基环树,显然要求的是一个最小支配集@poj3659。内向的基环树我们不好处理,考虑将这颗树反向,变成了外向基环树,这样我们可以随便找到环上的一个点以及指向它的点(首先每个联通快一定有环,但其实没有环的话就是正常的树了),不妨设为y->x,枚举x选择还是不选择,如果x选了,那么y就有人支配了,以x为根做正常的最小支配集即可。
做最小值配集的时候有一个小技巧tmp,有兴趣可以阅读下…
(还有一种直接贪心乱搞的做法在下面…)
#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>//by:MirrorGrayusing namespace std;const int N=1111111;int tot=-1,head[N],ver[N],nxt[N];int root,f[N],g[N],fa[N],vis[N],ban[N];void add(int x,int y){ nxt[++tot]=head[x]; head[x]=tot; ver[tot]=y;}int find(int x){ vis[x]=true; return vis[fa[x]]?x:find(fa[x]);}void dfs(int x){ vis[x]=true;f[x]=1;g[x]=0; int tmp=0x3f3f3f3f; for(int i=head[x];~i;i=nxt[i])if(ver[i]!=root){ dfs(ver[i]); f[x]+=min(f[ver[i]],g[ver[i]]); g[x]+=min(f[ver[i]],g[ver[i]]); tmp=min(tmp,f[ver[i]]-g[ver[i]]); } if(!ban[x])g[x]+=max(0,tmp);//强制选了root,fa[root]就有人管理了}int main(){ memset(head,-1,sizeof(head)); int n;scanf("%d",&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]){ int x=find(i); ban[fa[root=x]]=true; dfs(x);int tmp=f[x]; ban[fa[x]]=false; dfs(x);tmp=min(tmp,g[x]); ans+=tmp; } printf("%d\n",n-ans); return 0;}
考虑直接在原来的内向基环树上贪心,首先所有入度为0的点一定是要选的,他们所指向的点都不用选了,只要还没有进环,我们都可以本着只要指向它的点选了就不选它的贪心策略来处理,这个东西可以拓扑排序搞一下,最后处理环,现在有一个环,环上有一些点不用必须被支配,使得每个点要么选择要么被支配,乱搞吧…>_<…
(wa了无数遍发现我输出了最小支配集而不是最大被支配集…>_<…样例还能过…考场上小心啊…)
#include<queue>#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>//by:MirrorGrayusing namespace std;const int N=1111111;int cnt;int top,sta[N];int num[N],point[N],c[N];int in[N],size[N],vis[N],mark[N],p[N];struct Union_Find_Set{ int fa[N],size[N]; Union_Find_Set(){ for(int i=0;i<N;i++)fa[i]=i,size[i]=1; } int find(int x){ return fa[x]==x?x:fa[x]=find(fa[x]); } void merge(int a,int b){ a=find(a),b=find(b); if(size[a]>size[b])swap(a,b); if(a==b)return ;fa[a]=b; }}ufs;queue <int> q;void find_circle(int x){ vis[x]++; if(vis[x]==2)sta[++top]=x; if(vis[p[x]]!=2)find_circle(p[x]);}int main(){ int n;scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&p[i]),in[p[i]]++; ufs.merge(p[i],i); } for(int i=1;i<=n;i++)if(!mark[ufs.find(i)]){ mark[ufs.find(i)]=true; find_circle(i);size[++cnt]=top; while(top)num[sta[top--]]=cnt; } int ans=0; for(int i=1;i<=n;i++)if(!in[i])q.push(i); while(!q.empty()){ int x=q.front();q.pop(); bool fg=false; if(!c[x])ans++,fg=true,c[x]=true; int pos=p[x];in[pos]--; c[pos]|=fg; if(num[pos]&&c[pos])point[num[pos]]=pos; if(!in[pos]&&!num[pos])q.push(pos); } memset(mark,false,sizeof(mark)); for(int i=1;i<=n;i++)if(num[i]&&!mark[num[i]]){ mark[num[i]]=true; if(!point[num[i]])ans+=(size[num[i]]+1)>>1; else{ int pos=p[point[num[i]]]; while(pos!=point[num[i]]){ if(!c[pos])ans++,c[p[pos]]=true; pos=p[pos]; } } } printf("%d\n",n-ans); return 0;}//有向图最麻烦的是不能保证从任意一个点开始dfs都能遍历整个联通快 /*106 5 1 2 1 1 4 1 7 7*/
0 0
- BZOJ3037: 创世纪
- BZOJ3037: 创世纪
- BZOJ3037: 创世纪
- BZOJ3037 创世纪
- BZOJ3037: 创世纪
- [bzoj3037][贪心]创世纪
- BZOJ3037 创世纪 [贪心][树形DP]
- 创世纪
- 创世纪
- 创世纪
- 创世纪
- 创世纪
- 创世纪
- 创世纪
- 创世纪
- 创世纪
- bzoj3037--贪心
- #bzoj3037#无线电通信(DP)
- 电脑无法正常使用chm帮助文档的解决办法。
- Xutils 3.0使用详解
- linux字符驱动之中断按键
- poj3254 Corn Fields
- 請問 localtime() 為什麼不會造成 memory leak?
- BZOJ3037: 创世纪
- 无需安装插件 WordPress插入html5视频
- 关于C语言
- 字符串操作知识汇总
- Xcode 修改类前缀
- python sys模块
- Android 程序框架设计
- Anyplayer 1.0.5 - WordPress万能播放器插件
- 需求又改了