poj-1988-Cube Stacking (并查集)

 
Cube Stacking
Time Limit: 2000MS Memory Limit: 30000K
Total Submissions: 12537 Accepted: 4200
Case Time Limit: 1000MS

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:
moves and counts.
* In a move operation, Farmer John asks Bessie to move the stack containing cube X on top of the stack containing cube Y.
* In a count operation, Farmer John asks Bessie to count the number of cubes on the stack with cube X that are under the cube X and report that value.

Write a program that can verify the results of the game.

Input

* Line 1: A single integer, P

* Lines 2..P+1: Each of these lines describes a legal operation. Line 2 describes the first operation, etc. Each line begins with a 'M' for a move operation or a 'C' for a count operation. For move operations, the line also contains two integers: X and Y.For count operations, the line also contains a single integer: X.

Note that the value for N does not appear in the input file. No move operation will request a move a stack onto itself.

Output

Print the output from each of the count operations in the same order as the input file.

Sample Input

 

6
M 1 6
C 1
M 2 4
M 2 6
C 3
C 4

Sample Output

1
0
2

 

题意:有n个从1到n编号的箱子,将每个箱子当做一个栈,对这些箱子进行p次操作,每次操作分别为以下两种之一:
           1>输入 M x y:表示将编号为x的箱子所在的栈放在编号为y的箱子所在栈的栈顶.
           2>输入 C x:计算编号为x的所表示的栈中在x号箱子下面的箱子数目.

做法:这里我主要是用路径压缩来解决问题。
定义一个节点,每个节点存储的内容有:父亲,孩子,跳跃父亲,跳跃孩子,跳跃父亲长度,跳跃孩子长度。

 

 
 
 
这里举个例子:
假如我要把上图的X和Y合并,那么假设当前2个集合的情况如上图所示。
这时候我先找到X最底下的孩子、Y最上面的父亲。接下来我们要把两者合并。
1.把X和Y连接起来,那么X的跳跃孩子就是Y,Y的跳跃父亲就是X。跳跃长度就是图中的3+1+1.
2.把X最底下的孩子和Y连起来
3.把Y最上面的父亲和X连起来
这样就可以达到一个路径压缩的过程,在查找孩子和父亲的时候就可以大大减少搜索长度。
 
#include <iostream>
using namespace std;

struct node
{
	int fa,ch,up,dn,lu,ld;		//父亲,孩子,跳跃父亲,跳跃孩子,跳跃父亲长度,跳跃孩子长度
	node()
	{
		up=dn=lu=ld=-1;			//初始化不存在
	}
}num[30010];

int findf(int x,int &l)			//l记录了长度
{
	while(1)
	{
		if(num[x].up!=-1)		//跳跃查找
		{
			l+=num[x].lu;
			x=num[x].up;
		}
		else if(num[x].fa!=x)
		{
			x=num[x].fa;
			l++;
		}
		else
			break;
	}
	return x;
}

int findc(int x,int &l)
{
	while(1)
	{
		if(num[x].dn!=-1)			//跳跃查找
		{
			l+=num[x].ld;
			x=num[x].dn;
		}
		else if(num[x].ch!=x)
		{
			x=num[x].ch;
			l++;
		}
		else
			break;
	}
	return x;
}

void insert(int x,int y)
{
	int cl=0,fl=0;
	int ch=findc(x,cl);				//X最底下的孩子
	int fa=findf(y,fl);				//Y最上面的父亲
	num[ch].ch=fa;					//连接X最底下的孩子和Y最上面的父亲
	num[fa].fa=ch;
	num[x].dn=y;					//连接X,Y
	num[y].up=x;
	num[x].ld=num[y].lu=cl+fl+1;	//计算长度
	num[ch].dn=y;					//连接X最底下的孩子和Y
	num[ch].ld=fl+1;
	num[fa].up=x;					//连接Y最上面的父亲和X
	num[fa].lu=cl+1;
}
int cal(int x)
{
	int len=0;
	findc(x,len);
	return len;

}
int main()
{
	int m,x,y,i;
	char str[3];

	for(i=0;i<=30000;i++)			//初始化父亲孩子
		num[i].fa=num[i].ch=i;
	scanf("%d",&m);
	while (m--)
	{
		scanf("%s%d",str,&x);
		if(str[0]=='C')
		{
			 printf("%d\n",cal(x));
		}
		else
		{
			scanf("%d",&y);
			insert(x,y);
		}
	}
	return 0;
}
 
 
 
虽然这样可以优化时间,但是效果还不是很理想,500MS。
网上其他的做法可以参照:http://www.cppblog.com/koson/archive/2010/04/08/111946.aspx?opt=admin

 

 

 

 

 

 
 
 
 
 
 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值