目录
POJ 1703 Find them, Catch them // 种类并查集 自己的详细理解过程 回溯过程
POJ 1703 Find them, Catch them // 种类并查集 自己的详细理解过程 回溯过程
POJ 1611 The Suspects
Description
Severe acute respiratory syndrome (SARS), an atypical pneumonia of unknown aetiology, was recognized as a global threat in mid-March 2003. To minimize transmission to others, the best strategy is to separate the suspects from others.
In the Not-Spreading-Your-Sickness University (NSYSU), there are many student groups. Students in the same group intercommunicate with each other frequently, and a student may join several groups. To prevent the possible transmissions of SARS, the NSYSU collects the member lists of all student groups, and makes the following rule in their standard operation procedure (SOP).
Once a member in a group is a suspect, all members in the group are suspects.
However, they find that it is not easy to identify all the suspects when a student is recognized as a suspect. Your job is to write a program which finds all the suspects.
Input
The input file contains several cases. Each test case begins with two integers n and m in a line, where n is the number of students, and m is the number of groups. You may assume that 0 < n <= 30000 and 0 <= m <= 500. Every student is numbered by a unique integer between 0 and n−1, and initially student 0 is recognized as a suspect in all the cases. This line is followed by m member lists of the groups, one line per group. Each line begins with an integer k by itself representing the number of members in the group. Following the number of members, there are k integers representing the students in this group. All the integers in a line are separated by at least one space.
A case with n = 0 and m = 0 indicates the end of the input, and need not be processed.
Output
For each case, output the number of suspects in one line.
Sample Input
100 4
2 1 2
5 10 13 11 12 14
2 0 1
2 99 2
200 2
1 5
5 1 2 3 4 5
1 0
0 0
Sample Output
4
1
1
北大课件代码:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<queue>
#include<stdlib.h>
using namespace std;
const int maxn=30100 ;
int c[maxn];
int sum[maxn];
int n,m,k,p,w;
int find(int x)
{
if(c[x]==x)
return x;
return c[x]=find(c[x]);//缩短路径,不缩短路径会超时
}
void u(int a,int b)
{
int fx=find(a);
int fy=find(b);
if(fx!=fy)
c[fy]=fx;//将b这个分支挂在a这一分支中
sum[fx]+=sum[fy];//a这一分支多出的节点就是b这一分支中的节点数目
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
int cnt=0;
if(n==0 && m==0)
{
break;
}
for(int i=0;i<=n;i++)
{
c[i]=i;
sum[i]=1;
}
//scanf("%d",&k);
for(int i=0;i<m;i++)
{
scanf("%d%d",&p,&k);
for(int j=1;j<p;j++)
{
scanf("%d",&w);
u(k,w);
}
}
printf("%d\n",sum[find(0)]);//要把带有0的集合,缩短路径,然后输出
}
return 0;
}
注意:
1.要把带有0这一元素的集合记性路径压缩,然后再从sum数组中找和它同一个集合的元素个数。
自己之前代码:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<queue>
#include<stdlib.h>
using namespace std;
const int maxn=30100 ;
int c[maxn];
int b[maxn];
int n,m,k,p,w;
int find(int x)
{
/* 超时 没有缩短路径
if(c[x]==x)
return x;
else
return find(c[x]);
*/
//缩短路径是要把每一个节点的根节点都设置为祖先节点,这样在查找是否在相同集合的时候,直接找是否有共同的祖先节点即可
if(c[x]==x)
return x;
return c[x]=find(c[x]);
}
void u(int a,int b)
{
int fx=find(a);
int fy=find(b);
if(fx!=fy)
c[fx]=fy;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
int cnt=0;
if(n==0 && m==0)
{
break;
}
for(int i=0;i<=n;i++)
c[i]=i;
//scanf("%d",&k);
for(int i=0;i<m;i++)
{
scanf("%d%d",&p,&k);
for(int j=1;j<p;j++)
{
scanf("%d",&w);
u(k,w);
}
}
for(int i=0;i<n;i++)
{
if(find(i)==c[0])
cnt++;
}
printf("%d\n",cnt);
}
return 0;
}
注意:
1.不要忘记压缩路径,不压缩路径就会超时。
POJ 1988 Cube Stacking
Description Farmer John and Betsy are playing a game with N (1 <= N <= 30,000)identical cubes labeled 1 through N. They start with N stacks, each containing a single cube. Farmer John asks Betsy to perform P (1<= P <= 100,000) operation. There are two types of operations: Input * Line 1: A single integer, P Output Print the output from each of the count operations in the same order as the input file. Sample Input Sample Output Source |
用under数组来存放每一个方格下面的方格数,当我们要求某一个数的下面的方格数的时候,我们可以把当前这个数下面的求完的数据加起来即可。
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<queue>
#include<stdlib.h>
using namespace std;
const int maxn=30100;
int c[maxn];
int b[maxn];
int sum[maxn];
int under[maxn];
int n,m,k,p,w;
int find(int x)
{
if(c[x]==x)
return x;
int t=find(c[x]);
under[x]+=under[c[x]];//将第x个节点之前的下面的方块数加起来
c[x]=t;
return c[x];
/*
return c[x]=find(c[x]); //用这一句压缩路径,会使方格个数增加
*/
}
void u(int a,int b)
{
int fx=find(a);
int fy=find(b);
if(fx!=fy)
c[fy]=fx;
under[fy]=sum[fx]; //存放每一个节点的下面的方块数
sum[fx]+=sum[fy];
}
int main()
{
int p;
for(int i=0;i<maxn;i++)
{
sum[i]=1;
under[i]=0;
c[i]=i;
}
scanf("%d",&p);
for(int i=0;i<p;i++)
{
char s[20];
int a,b;
scanf("%s",s);
if(s[0]=='M')
{
scanf("%d%d",&a,&b);
u(b,a);
}
else
{
scanf("%d",&a);
find(a);
printf("%d\n",under[a]);
}
}
return 0;
}
注意:
1.u(b,a),存的是b,a,倒过来存放,这样才是存放的下面的方格数。
POJ 1182 食物链
Description
动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。
现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这N个动物所构成的食物链关系进行描述:
第一种说法是"1 X Y",表示X和Y是同类。
第二种说法是"2 X Y",表示X吃Y。
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
1) 当前的话与前面的某些真的话冲突,就是假话;
2) 当前的话中X或Y比N大,就是假话;
3) 当前的话表示X吃X,就是假话。
你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。
Input
第一行是两个整数N和K,以一个空格分隔。
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。
Output
只有一个整数,表示假话的数目。
Sample Input
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
Sample Output
3
Source
[Submit] [Go Back] [Status] [Discuss]
总结:起初做这个题目的时候,丝毫没有头绪,放了一段时间,再回头看的时候,感觉好了很多,下面来捋一捋思路,
对于这个题目,我先来说一下我的思路与做题所需要的辅助。
首先,这个题目的难点在于:结点与根节点之间的关系(0:是同类 1:被根吃 2:吃根),在寻找根结点的根的关系的时候,还存在剪枝的操作、结点之间关系的更新。难点也是这两点。
一、寻找根结点,剪枝操作
int find(int x)
{
if(p[x].pre==x)
return x;
int t=p[x].pre;
p[x].pre=find(t);
p[x].relation=(p[x].relation+p[t].relation)%3;
return p[x].pre;
}
详细的剪枝过程在 这篇博客中有所讲述 ::https://blog.csdn.net/lmengi000/article/details/80109392
二、节点之间关系的更新
1.
int root1 =find(a);
int root2 =find(b);
if(root1!=root2)
{
p[root2].pre=root1;
p[root2].relation=(3+(type-1)+p[a].relation-p[b].relation)%3;
}
当输入的两个结点不属于同一个集合的时候,这个时候它们的祖宗根是不同的,我们就要执行合并操作,
p[root2].pre=root1; 更新根,把两个不同的集合合并为一个集合
当集合合并了之后,还要再进一步判断两个合并的祖宗根之间的关系
p[root2].relation=(3+(type-1)+p[a].relation-p[b].relation)%3;
解释一下这一句代码,这一句代码困扰了我很久,其实理解了之后再加以数学知识,就感觉好很多,我们可以把这一句话想象成数学中的向量:
root1->root2 root1->x ; x->y ; y->root2 ,多出的就是x->y
AC代码:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 500100
struct node
{
int pre;
int relation;
}p[N];
int n,k;
int type;
int lies=0;
int find(int x)
{
if(p[x].pre==x)
return x;
int t=p[x].pre;
p[x].pre=find(t);
p[x].relation=(p[x].relation+p[t].relation)%3;
return p[x].pre;
}
void inist()
{
for(int i=1;i<=n;i++)
{
p[i].pre=i;
p[i].relation=0;
}
}
int union_set(int type,int a,int b)
{
int root1 =find(a);
int root2 =find(b);
if(root1!=root2)
{
p[root2].pre=root1;
p[root2].relation=(3+(type-1)+p[a].relation-p[b].relation)%3;
}
else
{
if(type==1 && p[a].relation!=p[b].relation)
{
return 1;
}
if(type==2 && (3-p[a].relation+p[b].relation)%3!=(type-1))
{
return 1;
}
}
return 0;
}
int main()
{
scanf("%d%d",&n,&k);
inist();
int a,b;
for(int i=1;i<=k;i++)
{
scanf("%d%d%d",&type,&a,&b);
if(a>n || b>n)
{
lies++;
continue;
}
else if(type==2 && a==b)
{
lies++;
continue;
}
else
{
lies=lies+union_set(type,a,b);
continue;
}
}
printf("%d\n",lies);
return 0;
}
大白书代码:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<queue>
#include<stdlib.h>
using namespace std;
const int maxn = 150100;
int c[maxn];
//int T[maxk],X[maxnk],Y[maxk];
int n,k;
int find(int x)
{
if(c[x]==x)
return x;
return c[x]=find(c[x]);
}
bool same(int a,int b)
{
return find(a)==find(b); //是否在同一个集合里
}
void u(int a,int b)
{
int fx=find(a);
int fy=find(b);
if(fx!=fy)
c[fx]=fy;
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=0;i<=3*n;i++)
{
c[i]=i;
}
int cnt=0;
int t,x,y;
for(int i=0;i<k;i++)
{
scanf("%d%d%d",&t,&x,&y);
//x,x+n,x+2*n 代表x-A,x-B,x-C
if(x>n||y>n)
{
cnt++;
continue;
}
if(t==1) //x与y是同类关系 x与y的A,B,C都要是同类关系
{
if(same(x,y+n)||same(x,y+2*n))
{
cnt++;
}
else
{
u(x,y);
u(x+n,y+n);
u(x+2*n,y+2*n);
}
}
else //x与y是吃的关系,既然是吃的关系,就会形成循环 A吃B,B吃C,C吃A
{
if(same(x,y)||same(x,y+2*n))
cnt++;
else
{
u(x,y+n);//A-B
u(x+n,y+2*n);//B-C
u(x+2*n,y);//C-A
}
}
}
printf("%d\n",cnt);
return 0;
}