解题报告之DisjointSet

szu:acm.szu.edu.cn
Problem A Locked Boxes
类别:中等
题意:给定每个钥匙所在的盒子(n个),问打开所有盒子需要破坏的锁的最小个数
题解:1、每个盒子可能由多个钥匙,2、每个钥匙只属于一个盒子。
满足不相交集合的性质。于是,如果存在子集S满足:
对任意的元素E,E的钥匙在盒子H里,盒子H属于S。
那么该集合只需要打破一个锁就可以打开所有盒子。
最终结果为子集个数

Problem B Reading books
类别:简单
题意:给出n本书的关系,同一集合的书,后读的只需一半时间,问读完的总时间
题解:技巧,小的做根

Problem C Network Connections
类别:简单
题意:某些时候连接两台电脑,某些时候询问两台电脑是否连接
最终输出yes,no的次数
题解:技巧,用ssanf输入

Problem D Sorting the candies
类别:困难
题意:n堆糖果,5种操作
1:合并两堆
2:将其中一堆合并到另一堆所在的集合
3:将给定的m堆独立成一个集合
4:询问两堆是不是同一集合
5:询问某堆所在集合的糖果堆数
题解:合并查询与一般的并查集相同,多出一个删除的操作。
1、暴力删除,自己想(超时)
2、用伪删除,原来的元素不动,追加新元素代替这个元素

Problem E Are they the same?
类别:困难
题意:
题解:这个题目主要考查:并查集+快速检索
预处理:开始将所有关系 以及 所有单词 全部存起来,
再对单词 排序去重。然后根据单词建立映射,根据关系划分集合。
接下来:对每次询问,切割单词找到单词所在的集合序号
存下来后得到两条数组,如果两条数组大小和内容均相同的话,
那么这两句就same了。
快速查找可以 建立字典树、hash 也可以用map关联,以及二分
速度:字典树<=>hash < 二分<=>map
注意: 单词有重复
string串长可能不一致、可能连续两个空格之类
记得快速检索
本来可以不用保证string里边的单词一定在前面出现过的!!!

Problem F Travel Problem
见http://blog.csdn.net/swrdlgc/article/details/7038416

附加:hdu:acm.hdu.edu.cn 1272 并查集判联通(技巧:一个集合)

Problem C Network Connections

#include <cstdio>
#include <cassert>
 
int ds[11000];
 
int father(int n)
{
	return (n==ds[n])?n:(ds[n]=father(ds[n]));
}
 
void bin(int a, int b)
{
	ds[father(a)]=father(b);
}
 
int main()
{
	char s[100], c; 
	int a, b, t, n, i, yes, no; 
 
	scanf("%d", &t);
	while(t--)
	{
		scanf("%d", &n);
		for(i=1; i<=n; ++i)	ds[i]=i;
		yes=no=0; gets(s);	
		while(gets(s))
		{
			if(!s[0]) break;
			sscanf(s, "%c %d %d", &c, &a, &b);
			if(c=='c') bin(a, b);
			else if(c=='q')	((father(a)==father(b))?(yes++):(no++));
		}
		printf("%d,%d\n", yes, no);
		if(t)	puts("");
	}
	return 0;
}


Problem D Sorting the candies

#include <cstdio>
#include <cassert>
#include <vector>
#include <iostream>
using namespace std;
 
#define GET(a) (v[a][v[a].size()-1])
#define REMOVE {s[cha(GET(a))]--; v[a].push_back(++n); f[n]=n; s[n]=1; v[n].push_back(n);}
 
const int N=50000;
int f[N], s[N];
vector<int> v[N];
 
int cha(int n)
{
	return (n==f[n])?n:(f[n]=cha(f[n]));
}
 
void bin(int a, int b)
{
	int fa=cha(a), fb=cha(b);
 
	if(fa==fb) return;
	if(s[fa]>s[fb]) swap(fa, fb);
	f[fa]=fb; s[fb]+=s[fa]; s[fa]=0;
}
 
int main()
{
	char c;	int n, m, a, b, i;
 
	while(~scanf("%d%d", &n, &m) && n+m)
	{
		for(i=1; i<=n; ++i) 
		{
			f[i]=i; s[i]=1;
			v[i].clear();
			v[i].push_back(i);
		}
		while(m--)
		{
			getchar(); getchar(); c=getchar();
			switch(c)
			{
			case 'A':
				scanf("%d%d", &a, &b);
				bin(GET(a), GET(b));
				break;
			case 'B':
				scanf("%d%d", &a, &b);
				REMOVE;	bin(GET(a), GET(b));
				break;
			case 'C':
				scanf("%d", &b);
				for(i=1; i<=b; ++i)
				{
					scanf("%d", &a); REMOVE;
				}
				for(i=1; i<b; ++i)
				{
					a=n-i;
					bin(GET(n), GET(a));
				}
				break;
			case 'D':
				scanf("%d%d", &a, &b);
				puts(cha(GET(a))==cha(GET(b))?"YES":"NO");
				break;
			case 'E':
				scanf("%d", &b);
				printf("%d\n", s[cha(GET(b))]);
				break;
			default: assert(0>1); break;
			}
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值