Huge input,scanf is recommended.
题意: 有一堆虫,正常来说只能和异性有关系,现在给你一堆关系,问你这些中可能存在“同性恋”吗
思路1: (种类并查集) 除了正常的并查集之外,此题还要维护一个关系数组记录它与其父亲的性别关系,这个关系一般自己定义,
此题可以认为是同性为 1 , 异性为0 ,那么要找到它与其父亲的父亲的关系就可以使(rank[x]+rank[x+1] )%2 ,依次递归下去就可以得到它和他最上面的祖先的关系
并查集一般就考虑并,和查两部分,其中压缩路径的时候要注意将关系数组也要更新,
1 #include <cstdio>
2 //存储的是其父亲的下表
3 int bugs[2010];
4 int relation[2010];//1:相同性别 0:不同性别
5 //初始化
6 void init(int len)
7 {
8 for(int i = 0;i <= len; i++)
9 {
10 bugs[i] = i;
11 relation[i] = 1;
12 }
13 }
14 //找到根
15 int find(int bug)
16 {
17 if(bugs[bug]==bug)return bug;
18 int tem = bugs[bug];
19 bugs[bug] = find(bugs[bug]);//递归更新域,返回最终的父亲节点,把所有的孩子都更新了
20 //注意这里,求当前位置和父亲的关系,记录之前父亲的位置为tem,然后因为是递归,
21 //此时的relation[tem]已经在递归中更新过了,也就是孩子和父亲的关系+父亲和爷爷的关系+1然后模2就得到
22 //孩子和爷爷的关系,这里用0和1表示,0表示不同性别,1表示相同性别
23 relation[bug] = (relation[bug]+relation[tem]+1)%2;
24 return bugs[bug];
25 }
26
27 void union_set(int a,int b,int x,int y)
28 {
29 //合并,让前边的集合的根指向后边集合的根,成为一个集合
30 bugs[x]=y;
31 //更新前边集合根和新的集合根之间的关系,
32 //注意这里,relation[a]+relation[x]与relation[b]
33 //相对于新的父节点必须相差1个等级,因为他们不是gay
34 relation[x] = (relation[b]-relation[a])%2; //这里的种类函数还是不理解,大概是根据一个关系的环状推出关系
35 }
36
37 int main()
38 {
39 int S;
40 int n,inter;
41 int bug1,bug2,parent1,parent2;
42 bool flag;//false:无同性恋,true:有同性恋
43 scanf("%d",&S);
44 for(int i=1; i<=S;i++)
45 {
46 scanf("%d%d",&n,&inter);
47 flag = false;
48 init(n);//初始化,使其父节点为自己
49 for(int j = 1; j <= inter; j++)
50 {
51 scanf("%d%d",&bug1,&bug2);
52 if(flag)continue;// 因为当有同性的时候是依次读入的数据,要保证数据时读完的所以用continue
53 parent1 = find(bug1);
54 parent2 = find(bug2);
55 if(parent1==parent2)
56 {
57 if(relation[bug1]==relation[bug2])//同性
58 flag = true;
59 }
60 union_set(bug1,bug2,parent1,parent2);
61 }
62 if(flag)
63 printf("Scenario #%d:\nSuspicious bugs found!\n",i);
64 else
65 printf("Scenario #%d:\nNo suspicious bugs found!\n",i);
66 printf("\n");
67 }
68 return 0;
69 }
下面是dfs的思路,将所有的关系都用链接表的形式存起来,然以后肯定会有环的情况,当时奇环的时候(定点数是基数的时候)就存在同性恋,要是偶环就是不存在同性恋,扫描的时候统计节点数,当再次扫描到同一个点的时候看扫描过得点数是奇还是偶。
1 #include <cstdio>
2 #include <cstring>
3 using namespace std;
4 #define N 2005
5 #define M 1000005
6
7 int head[N];
8 struct Edge{
9 int v, next;
10 }edge[2*M];
11 int Ecnt;
12
13 void init()
14 {
15 Ecnt = 0;
16 memset(head, -1, sizeof(head));
17 }
18
19 void add(int u, int v)
20 {
21 edge[Ecnt].v = v;
22 edge[Ecnt].next = head[u];
23 head[u] = Ecnt++;
24 edge[Ecnt].v = u;
25 edge[Ecnt].next = head[v];
26 head[v] = Ecnt++;
27 }//存双向边
28
29 bool visited[N];
30 int f[N];//统计它是男生还是女生,定义 男生是1 女生是0 搜索的时候按0,1,0,1 的顺序标记点,如果再次访问到原先的点的时候应该按顺序标的值与其之前标的不一样的话就是奇环
31 bool dfs(int u)
32 {
33 for(int i = head[u]; i != -1; i = edge[i].next)//遍历每一条边
34 {
35 int v = edge[i].v;//下一个点
36 if(!visited[v])//如果没有访问过这个点
37 {
38 visited[v] = 1;
39 f[v] = (1-f[u]); //按0,1 交替顺序的标记点
40 bool res = dfs(v);//如果在这之前就已经发现有奇环的话就直接返回FALSE
41 if(res == false) return false;
42 }
43 else if(f[u] == f[v]) return false;//最后又访问到了这个点,就判断这两个点是否是同一个值
44 }
45 return true;
46 }
47
48 int main()
49 {
50 int T, n, m;
51 scanf("%d", &T);
52 int cas = 1;
53 while(T--)
54 {
55 printf("Scenario #%d:\n", cas++);
56 scanf("%d %d", &n, &m);
57 init();
58 int s, t;
59 for(int i = 0; i < m; i++)
60 {
61 scanf("%d %d", &s, &t);
62 add(s, t);
63 }
64 memset(visited, 0, sizeof(visited));
65 memset(f, -1, sizeof(f));
66 bool ans = true;
67 for(int i = 1; i <= n; i++)
68 {
69 if(!visited[i])
70 {
71 visited[i] = 1;
72 f[i] = 1;
73 bool res = dfs(i);
74 if(res == false)
75 {
76 ans = false;
77 break;
78 }
79 }
80 }
81 if(ans) puts("No suspicious bugs found!\n");
82 else puts("Suspicious bugs found!\n");
83 }
84 return 0;
85 }