ZJYYCOJ 好基友(贪心/并查集)

  • 题面描述
    每个班级总有几对好基友,创意班也不例外,最典型的就是汪李弟弟组合。现在有N对基友随机的坐在一排连续的座位上,好基友们总是希望能坐在一起,现在请你帮帮忙,计算一下最少要交换多少次(每两个人交换一次位置算作1次),以便让每对好基友都并肩坐在一起。
    人的编号为 0 —— 2N - 1
    座位的编号为 0 —— 2N - 1
    每对好基友按照编号顺序来确定,(4,5)为好基友,(8,9)为好基友 , (0,1)为好基友

  • 输入
    样例输入由多组测试样例组成,第一行输入一个整数N(1 <= N <= 100000),代表有N对基友。第二行输入2N个整数,代表每个位置初始上的人编号

  • 输出
    输出最少交换次数

  • 样例输入

  • 2
    0 2 1 3
    2
    3 2 0 1

  • 样例输出

  • 1
    0

  • 解题思路(偷懒使用官方解题思路啦)

  1. 贪心思路:
    根据我们的假设,可以制定按顺序让每张沙发上基友开心的策略。 对于每张沙发,找到沙发上第一个人的基友,如果不在同一个沙发 上,就把沙发上的第二人换成第一个人的基友。
    如果一个人的编号为 x,那么他的基友的编号为 x ^ 1, ^ 在这里 是异或操作。对于每张沙发上的第一个人 x = row[i],找到他们的 同伴所在的位置 row[j],将 row[j] 和 row[i + 1] 互相交换。
  2. 并查集思路:
    我们设想一下加入有两对基友互相坐错了位置,我们至多只需要换 一次。 如果三对基友相互坐错了位置,A1+B2,B1+C2,C1+A2。他们三个之 间形成了一个环,我们只需要交换两次。 如果四队基友相互坐错了位置,即这四对基友不与其他基友坐在一 起,A1+B2,B1+C2,C1+D2,D1+A2.他们四个之间形成了一个环,他们 只需要交换三次并且不用和其他基友交换,就可以将这四对基友交 换好,
    以此类推,其实就是假设 k 对基友形成一个环状的错误链,我们只 需要交换 k - 1 次就可以将这 k 对基友的位置排好。 所以问题转化成 N 对基友中,有几个这样的错误环。 我们可以使用并查集来处理,每次遍历相邻的两个位置,如果他们 本来就是基友,他们处于大小为 1 的错误环中,只需要交换 0 次。 如果不是基友,说明他们呢两对处在同一个错误环中,我们将他们 合并(union),将所有的错坐基友合并和后,答案就是基友对 - 环个数。
    这也说明,最差的情况就是所有 N 对基友都在一个环中,这时候我 们需要N - 1调换。
    最好情况每对基友已经坐好了,已经有 N 个大小为 1 的环,这时候 我们需要N - N次调换。

贪心代码

#include <bits/stdc++.h>

using namespace std;
int a[200005], pos[200005];

int main()
{
	int n;
	while(~scanf("%d", &n))
	{
		int sum = 0;  // 交换次数
		for(int i = 1; i <= 2 * n; i++)
		{
			scanf("%d", &a[i]);
			pos[a[i]] = i;  // 记录每个同学的位置
		}
		for(int i = 1; i <= 2 * n; i += 2)
		{
			int temp = a[i] ^ 1;  // 第i个位置的基友
//			cout << temp << endl;
			if(a[i + 1] != temp)
			{
				a[pos[temp]] = a[i + 1];  // 序号互换
				a[i + 1] = temp;
				pos[a[pos[temp]]] = pos[temp];  // 位置互换
				pos[temp] = i + 1;
				sum++;
			}
		}
		printf("%d\n", sum);
	}
	return 0;
 } 

贪心这里还是WA了两次,是因为只交换了两个位置上的两个序号但没有交换这两个序号的位置。

并查集代码

#include <bits/stdc++.h>

using namespace std;
int fa[200005], sum[200005], a[200005];
int findx(int r)
{
   int temp, k = r;
   while(r != fa[r])
   	r = fa[r];
   //路径压缩
   while(k != r)
   {
   	temp = fa[k];
   	fa[k] = r;
   	k = temp;
   }
   return r;
}
void merge(int x, int y)
{
   int xx = findx(x), yy = findx(y);
   if(xx != yy)
   	fa[xx] = yy;
}

int main()
{
   ios::sync_with_stdio(false);
   int n, ans;
   while(cin >> n)
   {
   	ans = 0;
   	memset(sum, 0, sizeof(sum));
   	for(int i = 0; i < 2 * n; i++)
   		fa[i] = i;
   	for(int i = 0; i < 2 * n; i++)
   	{
   		cin >> a[i];
   		if(i % 2)
   		{
   			merge(i, i - 1);  // 合并基友
   			merge(a[i], a[i - 1]);  // 合并一个沙发上的同学
   		}
   	}
   	int temp;
   	for(int i = 0; i < 2 * n; i++)
   	{
   		temp = findx(i);
   		sum[temp]++;  // 将所有同学统计到同一个环的祖先上用于计算
   	}
   	for(int i = 0; i < 2 * n; i++)
   	{
   		if(sum[i])
   			ans += (sum[i] / 2 - 1);  // 同一个换需要交换(sum[i] / 2 - 1)次
   	}
   	cout << ans << endl;
   }
   return 0;
}
发布了3 篇原创文章 · 获赞 4 · 访问量 97

猜你喜欢

转载自blog.csdn.net/Melorrr/article/details/104563524
今日推荐