POJ 1703 Find them, Catch them

题目大意:

        Tadu城的警察想和混乱说拜拜,准备行动起来铲除城中的两大黑帮——龙帮和蛇帮,但首先他们得弄清楚罪犯到底属于哪个帮才能准确定罪,现在的问题是给你两个罪犯,你能否判断他们两是否属于同一帮。

        现有多个测例(测例数题中会给出),假设每个测例中会告诉你罪犯总共有N个(N ≤ 100,000,其编号为1-N),每个罪犯只属于两个帮派中的一个,接下来会有M条信息给你处理:

                D a b

                表示a和b属于不同帮派;

                A a b

                让你给出判断,判断a和b是否属于同一帮派(也有可能无法判断,依据前面给出的信息来判断);

题目链接

思路一:将D信息中的两个罪犯合并在同一集合中,表示(a, b)对被考察过了,然后利用各自和并查集根的关系判断他们是否在同一帮派中。

注释代码:

/*         
 * Problem ID : POJ 1703 Find them, Catch them
 * Author     : Lirx.t.Una         
 * Language   : C++      
 * Run Time   : 297 ms         
 * Run Memory : 616 KB         
*/  

#include <string.h>
#include <stdio.h>

//maximum number of criminals,罪犯的最大数量
//100,000 + 1
#define	MAXN		100001

#define	TRUE		1
#define	FALSE		0

int		set[MAXN];//考查集合,将所有考查过的罪犯纳入一个集合
char	r[MAXN];//relationship,每个罪犯和根结点的关系
                //若和根结点在同一帮派则为TRUE,否则为FALSE

int
find(int x) {
	
	if ( x == set[x] )
		return x;
	
	int		f;
	
	f		= set[x];//保存x的老根
	set[x]	= find(f);//对x路径压缩
	//由于x被路径压缩了,所以其根结点改变了
	//接在新的根结点上后需要更新它和新根的关系
	//新关系可以由它跟老根的关系和老根和新根的关系推出
	  //(因为这是递归进行的,因此老根在上面递归结束后必然接在了现在的新根上,
	  //并且它跟新根的关系已经递归地更新过了,所以在这一层上可以放心利用老根和
	  //新根的关系)
	//若它俩关系相同那么必然x和新根在同一帮派,否则就处于不同帮派
	r[x] 	= !( r[x] ^ r[f] );
	
	return set[x];
}

int
main() {
	
	int		t;//测例数
	int		n;//罪犯总数
	int		m;//信息总数
	
	int		i;//计数变量
	
	char	cmd;//信息中的命令部分
	
	int		a, b;//信息中的两个罪犯的编号
	
	int		sa, sb;//set of a and b,a和b的根结点(即所属的集合)
	
	scanf("%d", &t);
	while ( t-- ) {
		
		scanf("%d%d", &n, &m);

		//由于初始化时每个罪犯的信息未知,所以每个人都归到以自己
		//为编号的集合中,因此自己就是根结点,自己和根结点必然都
		//同属于一个帮派,所以都初始化为TRUE
		memset(r + 1, TRUE, n * sizeof(char));
		
		//并查集初始化
		for ( i = 1; i <= n; i++ )
			set[i] = i;
		
		while ( m-- ) {
			
			scanf("\n%c%d%d", &cmd, &a, &b);
			
			sa = find(a);
			sb = find(b);
			
			switch (cmd) {
				
			case 'A' :
				
				if ( sa != sb ) {//代表a、b对还没有考察过
					
					puts("Not sure yet.");
					break;
				}
				
				if ( r[a] == r[b] ) {//现a、b同属一个集合了
					           //若a、b和各自的根(两根相同)的关系
					           //相同,则他俩必定在同一帮派中
					
					puts("In the same gang.");
					break;
				}
				
				puts("In different gangs.");//否则必定不在一个帮派中
				break;
				
			default ://若为D
				
				if ( sa != sb ) {//a、b不在一个集合中
					      //这就说明a、b对还没有被考察过
					
					//以下对a、b两个所属的集合进行合并
					set[sa] = sb;//sa的根发生变化(本来sa的根就是自己)

                    //sa换了新根后需要对其和新根的关系进行更新
					//该关系可以从a和b的关系,以及a、b各自的老根的关系推出
					//由于a和b必定不在一个帮派中
					//所以如果r[a]、r[b]相同,则r[sa]必定为FALSE,否则为TRUE
					r[sa] = !!( r[a] ^ r[b] );
					
					break;
				}
			}
		}
	}
	
	return 0;
}
无注释代码:

#include <string.h>
#include <stdio.h>

#define	MAXN		100001

#define	TRUE		1
#define	FALSE		0

int		set[MAXN];
char	r[MAXN];

int
find(int x) {
	
	if ( x == set[x] )
		return x;
	
	int		f;
	
	f		= set[x];
	set[x]	= find(f);
	r[x] 	= !( r[x] ^ r[f] );
	
	return set[x];
}

int
main() {
	
	int		t;
	int		n;
	int		m;
	
	int		i;
	
	char	cmd;
	
	int		a;
	int		b;
	
	int		sa;
	int		sb;
	
	scanf("%d", &t);
	while ( t-- ) {
		
		scanf("%d%d", &n, &m);
		memset(r + 1, TRUE, n * sizeof(char));
		
		for ( i = 1; i <= n; i++ )
			set[i] = i;
		
		while ( m-- ) {
			
			scanf("\n%c%d%d", &cmd, &a, &b);
			
			sa = find(a);
			sb = find(b);
			
			switch (cmd) {
				
			case 'A' :
				
				if ( sa != sb ) {
					
					puts("Not sure yet.");
					break;
				}
				
				if ( r[a] == r[b] ) {
					
					puts("In the same gang.");
					break;
				}
				
				puts("In different gangs.");
				break;
				
			default :
				
				if ( sa != sb ) {
					
					set[sa] = sb;
					r[sa] = !!( r[a] ^ r[b] );
					
					break;
				}
			}
		}
	}
	
	return 0;
}

思路二:敌人的敌人就是朋友

注释代码:

/*          
 * Problem ID : POJ 1703 Find them, Catch them 
 * Author     : Lirx.t.Una          
 * Language   : C++       
 * Run Time   : 297 ms          
 * Run Memory : 912 KB          
*/    

#include <stdio.h>

//前一半表示罪犯所在帮派,后一半表示罪犯的对手所在的帮派
#define	MAXN		200001

int		set[MAXN];

int
find(int x) {

	if ( x == set[x] )
		return x;

	return set[x] = find( set[x] );
}

void
union_set( int sx, int y ) {

	int		sy;

	sy = find(y);
	if ( sx == sy )
		return ;

	set[sx] = sy;
}

int
main() {

	int		t;
	int		n;
	int		m;
	int		i;
	int		a, b;
	int		sa, sb;

	char	cmd;

	scanf("%d", &t);
	while ( t-- ) {

		scanf("%d%d", &n, &m);
		for ( i = 1; i <= n + n; i++ )
			set[i] = i;

		while ( m-- ) {
		
			scanf("\n%c%d%d", &cmd, &a, &b);

			sa = find(a);
			sb = find(b);
			switch (cmd) {
			
				case 'A' :


					if ( sa == sb ) {
					
						puts("In the same gang.");
						break;
					}

					if ( sa == find( b + n ) ) {//若a和b的对手在同一帮派
						                      //则表示a、b肯定在不同帮派
					
						puts("In different gangs.");
						break;
					}

					puts("Not sure yet.");
					break;

				default :

					union_set( sa, b + n );//将a和b的对手合并到一个帮派
					union_set( sb, a + n );//同时也需要将b和a的对手合并到一个帮派
					break;
			}
		}
	}

	return 0;
}
无注释代码:

#include <stdio.h>

#define	MAXN		200001

int		set[MAXN];

int
find(int x) {

	if ( x == set[x] )
		return x;

	return set[x] = find( set[x] );
}

void
union_set( int sx, int y ) {

	int		sy;

	sy = find(y);
	if ( sx == sy )
		return ;

	set[sx] = sy;
}

int
main() {

	int		t;
	int		n;
	int		m;
	int		i;
	int		a, b;
	int		sa, sb;

	char	cmd;

	scanf("%d", &t);
	while ( t-- ) {

		scanf("%d%d", &n, &m);
		for ( i = 1; i <= n + n; i++ )
			set[i] = i;

		while ( m-- ) {
		
			scanf("\n%c%d%d", &cmd, &a, &b);

			sa = find(a);
			sb = find(b);
			switch (cmd) {
			
				case 'A' :


					if ( sa == sb ) {
					
						puts("In the same gang.");
						break;
					}

					if ( sa == find( b + n ) ) {
					
						puts("In different gangs.");
						break;
					}

					puts("Not sure yet.");
					break;

				default :

					union_set( sa, b + n );
					union_set( sb, a + n );
					break;
			}
		}
	}

	return 0;
}

单词解释:

chaos:n, 混乱

launch:vt, 发射

launch action:采取行动,启动任务

root up:vt, 根除,肃清

gangs:n, 帮派,黑帮

criminal:n, 罪犯

clan:n, 集团,部落

incomplete:adj, 不完全的,不完备的

gangster:n, 歹徒

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值