本文主要是介绍Hdu 1892Poj 2492 A Bug's Life[判断二分图 || 种类并查集],希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
昨天一天就弄着一道题了。
一开始的时候想法是判断是否存在奇数圈。如果存在,肯定有同性恋存在。
后来看到了别人的想法。就是,判二分图。
后来在,上课翻离散书的时候看到一个定理:n阶无向图是一个二分图当且仅当图中没有无奇数圈。
这样,判奇数圈和判二分图就是一个意思了。
那么,怎么来判奇数圈或者二分图呢???
搜索了一下,看到一种染色法判断二分图。意思就是,将图中的节点染色,如果能够把所有的点染成不同的两个颜色,并且不产生矛盾(相连的节点颜色相同);
如图所示:
可以用BFS或DFS实现:
BFS:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#define CLR(arr,val) memset(arr,val,sizeof(arr))
using namespace std;const int N=2005;
int color[N],n,m;
bool vis[N];
vector<int> map[N];bool bfs(int s){queue<int> q;q.push(s);color[s]=1;while(!q.empty()){int now=q.front();q.pop();for(int i=1;i<=10000000;i++);printf("now %d\n",now);if(!vis[now]){int len=map[now].size();for(int i=0;i<len;i++){int des=map[now][i];q.push(des);if(color[des]==-1)color[des]=color[now]==0?1:0;else {if(color[des]==color[now]) return false;else continue;}}vis[now]=true;}}return true;
}int main(){freopen("1.txt","r",stdin);int T,k=1;scanf("%d",&T);while(T--){CLR(color,-1);CLR(vis,0);CLR(map,0);scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){int a,b;scanf("%d%d",&a,&b);map[a].push_back(b);map[b].push_back(a);}int i;printf("Scenario #%d:\n",k++);for(i=1;i<=n;i++){if(!vis[i]){bool flag=bfs(i);if(!flag){printf("Suspicious bugs found!\n\n");break;}}}if(i>n) printf("No suspicious bugs found!\n\n");}return 0;
}
DFS:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#define CLR(arr,val) memset(arr,val,sizeof(arr))
using namespace std;const int N=2003;
int n,m,color[N];
vector<int> map[N];
bool vis[N];bool dfs(int s){vis[s]=true;int len=map[s].size();for(int i=0;i<len;i++){int des=map[s][i];if(color[des]==color[s]) return false;if(color[des]==-1){color[des]=color[s]==0?1:0;if(!dfs(des)) return false;}}return true;
}
int main(){freopen("1.txt","r",stdin);int T,k=1;scanf("%d",&T);while(T--){CLR(map,0);CLR(vis,0);CLR(color,-1);scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){int a,b;scanf("%d%d",&a,&b);map[a].push_back(b);map[b].push_back(a);}printf("Scenario #%d:\n",k++);bool flag=false;for(int i=1;i<=n;i++){if(!vis[i]){color[i]=1;if(!dfs(i)){flag=true;break;}}}if(flag) printf("Suspicious bugs found!\n\n");else printf("No suspicious bugs found!\n\n");}return 0;
}
无论BFS还是DFS,需要注意的是,所有的节点可能出现在不同的集合中。需要多次BFS或DFS。
这个问题大多数还是用并查集做的。。表示很有压力。。
可以说是并查集的一种应用。。。大多数网上说是种类并查集。。。也就是说分类了。。网上找了一种说法。很是简单易于理解。。
下面一种方法是种类并查集。终于明白原理是为什么了。种类并查集是给每个结点一个权值。然后在合并和查找的时候根据情况对权值来进行维护。对于这个题来说,就可以给每个节点设置一个0或1的权值。0代表与根节点同性,1代表与根节点异性。与根节点的关系可以在查找的时候利用递归从根节点向叶子不断进行维护。利用抵消的原理,不断相加余2就行了。然后合并的时候,由于根节点的性别是不确定的,不能简单简单的合并就完了,还要考虑两颗树的根节点之间的关系。这时候,要想使两个异性结点一个为0一个为1,
只要将被合并的那棵树的根节点维护一下就可以,下面的所有的结点的值都可以在查找的时候由这个根节点来决定。因为,如果根节点为0,那么下面的都不会改变,如
果为1的话,那下面的都会改变。剩下的就是考虑这个根节点要怎么确定。的时候由这个根节点来决定。因为,如果根节点为0,那么下面的都不会改变,如果为1的话,那下面的都会改变。剩下的就是考虑这个根节点要怎么确定。要想使输入两个异性结点一个为0一个为1,假如此时在两颗树中显示的是同性,那么只要把根节点设置成1,否则,就设置成0.这地方应该很好想,就不解释了。这时
可以用两者权值之和加1余2来实现。
上面的解释很是给力。。从上面可以看出好的解释,都是思路明了,句句有用。。。表示自己还没有达到这种情况。。 努力去达到这种程度吧。。
加一图更易于了解。。
Code:
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdio>
using namespace std;const int N = 1e3 * 2 + 5;
int father[N], sex[N];
int n, m;void Init()
{for(int i = 1; i <= n; i ++){father[i] = i;sex[i] = 0;}
}int find(int x)
{if(x != father[x]){int tmp = father[x];father[x] = find(father[x]);sex[x] = (sex[x] + sex[tmp]) % 2;}return father[x];// must return father[x]..
}bool Union(int x, int y)
{int a = find(x);int b = find(y);if(a == b && sex[x] == sex[y]) return true;else {father[a] = b;sex[a] = (sex[x] + sex[y] + 1) % 2;}return false;
}int main()
{int T, k = 0;scanf("%d", &T);while(T --){scanf("%d %d", &n, &m);Init();int x, y;bool flag = false;for(int i = 1; i <= m; i ++){scanf("%d %d", &x, &y);if(flag) continue;if(Union(x, y)) flag = true;}printf("Scenario #%d:\n", ++ k);if(flag) puts("Suspicious bugs found!\n");else puts("No suspicious bugs found!\n");}return 0;
}
种类并查集。get。。
这篇关于Hdu 1892Poj 2492 A Bug's Life[判断二分图 || 种类并查集]的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!