10月12日 并查集(Cube Stacking)

问题描述:

cube stacking

N个方块,编号为1-N,现在有P个操作,分为两种:
1 M x y表示有x的那一堆放到有y的那一堆上面
2 C x表示询问在x下方有多少个方块
N<=30000,p<=10^5
第一行输入操作次数P。
输入样例:
6
M 1 6
C 1
M 2 4
M 2 6
C 3
C 4
输出样例:
1
0

2


并查集大意就是开一个数组,储存每个元素的前驱,这在dijkstra算法中似乎用到过。

来看这道题目

知道并查集之后把这个题目画了画图,ok,so easy,不就是改几个前驱嘛…然后带着电脑带了一堆作业到图书馆,然后…作业一点没动

如图,画个图就好理解了


如果要严格的模拟这个过程,move时,并查集的底端就要接到另一个盒子集合的顶端,但是我们定义的并查集只能往下搜索,想到的解决方法如下:

1 开一个top数组,存放当前所在集合的top元素。这意味着遍历、遍历、遍历…越到后面越变态,时间可以爆炸了

2 往上搜索,每次都要遍历,一遍一遍对自己说:“不能遍历,不能把数模的习惯带过来…”

无数次阻止自己遍历的欲望之后,我们只好接受并查集合并了,就是只能接到另一个集合的根部。

这里我的解决办法是定义一个skip修正这种接法对真实情况的歪曲,每次的合并目标如果本身大小大于一,就要改变并上去的集合根部的skip,每次询问只要上溯到根部,再加上每次的skip就好了。

本题盒子集合不可以拆开(也就是永远是越堆越大),所以这么做是可行的,不过如果可以随便拆的话,网上书上的解法也得失效,就题论题。

#include<iostream>
using namespace std;
int cube[30000];//main中先把它初始化为根
int skip[30000];//连接跳跃
int at[30000];//根部所在集合的盒子数
int find(int x)//注意编号从1开始
{
	while (cube[x - 1] != x)
	{
		x = cube[x - 1];
	}
	return x;
}
void move(int x, int y)//每次都直接接在根上,然后相加
{
	int stackbottom = find(x);
	//合并
	int target = find(y);
	cube[stackbottom - 1] = target;
	skip[stackbottom - 1] = at[target-1]-1;//跳跃数
	at[target - 1] += at[stackbottom - 1];
	at[stackbottom - 1] = 1;//记为0更具有统一性,但对于编程不方便
}
int ques(int x)
{
	int count=0;
	while (cube[x - 1] != x)
	{
		count++;
		count += skip[x - 1];
		x = cube[x - 1];
	}
	return count;
}
int main()
{
	//初始化
	for (int i = 0; i < 30000; i++)
	{
		cube[i] = i + 1;
		skip[i] = 0;
		at[i] = 1;
	}
	int P;//操作数
	char code;//操作类型
	cin >> P;
	while (P--)
	{
		cin >> code;
		if (code == 'M')
		{
			int x, y;
			cin >> x >> y;
			move(x, y);
		}
		if (code == 'C')
		{
			int z;
			cin >> z;
			cout << ques(z) << endl;
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_viceversa

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值