BZOJ3037: 创世纪
2016-04-08 09:13
447 查看
题目大意:给定一个有向图,每个点出度为1,让你将这些点分为两个集合x和y,使得对于每一个y集合内的点都存在一个x集合内的点指向它,并且使得y集合尽可能大。
原图给的是一些内向基环树,显然要求的是一个最小支配集@poj3659。内向的基环树我们不好处理,考虑将这颗树反向,变成了外向基环树,这样我们可以随便找到环上的一个点以及指向它的点(首先每个联通快一定有环,但其实没有环的话就是正常的树了),不妨设为y->x,枚举x选择还是不选择,如果x选了,那么y就有人支配了,以x为根做正常的最小支配集即可。
做最小值配集的时候有一个小技巧tmp,有兴趣可以阅读下…
(还有一种直接贪心乱搞的做法在下面…)
考虑直接在原来的内向基环树上贪心,首先所有入度为0的点一定是要选的,他们所指向的点都不用选了,只要还没有进环,我们都可以本着只要指向它的点选了就不选它的贪心策略来处理,这个东西可以拓扑排序搞一下,最后处理环,现在有一个环,环上有一些点不用必须被支配,使得每个点要么选择要么被支配,乱搞吧…>_<…
(wa了无数遍发现我输出了最小支配集而不是最大被支配集…>_<…样例还能过…考场上小心啊…)
原图给的是一些内向基环树,显然要求的是一个最小支配集@poj3659。内向的基环树我们不好处理,考虑将这颗树反向,变成了外向基环树,这样我们可以随便找到环上的一个点以及指向它的点(首先每个联通快一定有环,但其实没有环的话就是正常的树了),不妨设为y->x,枚举x选择还是不选择,如果x选了,那么y就有人支配了,以x为根做正常的最小支配集即可。
做最小值配集的时候有一个小技巧tmp,有兴趣可以阅读下…
(还有一种直接贪心乱搞的做法在下面…)
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> //by:MirrorGray using namespace std; const int N=1111111; int tot=-1,head ,ver ,nxt ; int root,f ,g ,fa ,vis ,ban ; 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:MirrorGray using namespace std; const int N=1111111; int cnt; int top,sta ; int num ,point ,c ; int in ,size ,vis ,mark ,p ; struct Union_Find_Set{ int fa ,size ; 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都能遍历整个联通快 /* 10 6 5 1 2 1 1 4 1 7 7 */
相关文章推荐
- 收藏
- 网络层概述.虚电路网络和数据报网络
- Xutils 3.0使用详解
- ms-repeat 循环
- hdu 2037 今年暑假不AC (java)
- Access restriction: The type 'RSACipher' is not API
- JS异步编程
- 详解HttpURLConnection
- NYOJ 36 最长公共子序列
- smartforms不输出0
- 开放-封闭原则(OCP)
- 飞船汇合
- Android开发屏幕适配教程
- 0408 队友评价--一起做汉堡
- Jquery元素追加和删除
- 【Android】Android中期项目设计题目-界面设计小作业-提交截止时间2016.4.8
- PostgreSQL 日期格式化
- 浅谈SOA面向服务化编程架构(dubbo)
- 2016年深圳市宝安区小一学位申请流程及时间安排
- 5_3 安迪的第一个字典(UVa10815)<set的使用>