765. 情侣牵手

题目描述

n对情侣坐在连续排列的 ```2n ``个座位上,想要牵到对方的手。

人和座位由一个整数数组 row表示,其中 row[i]是坐在第 i 个座位上的人的 ID。情侣们按顺序编号,第一对是 (0, 1),第二对是 (2, 3),以此类推,最后一对是 (2n-2, 2n-1)

返回 最少交换座位的次数,以便每对情侣可以并肩坐在一起。 每次交换可选择任意两人,让他们站起来交换座位。

做题情况

  1. 做出来且思路与标答一致
  2. 做出来但思路较为复杂
  3. 有思路,但时间复杂度较高无法通过
  4. 没有思路 ☑

自己的想法:

怎么说呢,感觉有点畏难情绪。但其实有一些思路的已经。首先是那种不用换位置就已经是情侣连坐的,我们就不需要考虑了。
对于其他情况,对于两对相邻位置的四个人,如果交换一下,就可以使两对情侣都相邻了。
但对于其他的情况,显然是要绕一个大圈来换。
自己的疑惑,或者说没有绕过来的地方是:如果存在一个圈,那么是否存在更小的圈,使得最终交换的次数更少呢。
于是就卡在这里了。

标答:并查集

实际上,自己已经想了很大部分了。剩下的就是证明不存在更小的圈,以及编写程序了。
对于前者,因为显然当前这对相邻的两个人都在圈里面。因此我们可以根据他们的情侣逐步向两边展开。需要注意的是,这个展开是固定的,因为我们相邻的人是固定的,情侣是固定的,一定是相邻情侣这样交替展开的。所以到了最后形成的圈一定是固定的,也就不存在存在更小的圈的情况了。
至于编写程序,我们就循环进行这个展开过程,以及用集合来表示剩下的点就可以了。

实际代码

class Solution {
public:
    int neighbor_man(const int man, vector<int>& row, vector<int>& pos)
    {
        int neighbor;
        if (pos[man] % 2 == 0) return row[pos[man] + 1];
        else return row[pos[man] - 1];
    }
    int wife_man(const int man, vector<int>& row, vector<int>& pos)
    {
        int wife;
        if (man % 2 == 0) return man + 1;
        else return man - 1;
    }

    int minSwapsCouples(vector<int>& row)
    {
        //想明白了,其实两者是一样的
        //还有一个点是不需要想是否有更小的圈导致更少的交换次数,因为当前的两个人一定都在这个圈里,然后向两边扩展,最终得到的圈是唯一的
        int n = row.size();
        vector<int> pos(n);
        unordered_set<int> un_set;
        int res = 0;
        for (int i = 0; i < n; i += 2)
        {
            pos[row[i]] = i;
            pos[row[i + 1]] = i + 1;
            if (row[i] % 2 == 0)
            {
                if (row[i + 1] != row[i] + 1)
                {
                    un_set.insert(row[i]);
                    un_set.insert(row[i + 1]);
                    res++;
                }
            }
            else
            {
                if (row[i + 1] != row[i] - 1)
                {
                    un_set.insert(row[i]);
                    un_set.insert(row[i + 1]);
                    res++;
                }
            }
        }
        while (!un_set.empty())
        {
            auto it = un_set.begin();
            int man = *it;
            int woman = wife_man(man, row, pos);
            un_set.erase(man);
            un_set.erase(woman);
            while (1)
            {
                int neighbor = neighbor_man(man,row,pos);
                int woman_neighbor = wife_man(neighbor, row, pos);
                un_set.erase(neighbor);
                un_set.erase(woman_neighbor);
                if (neighbor_man(woman_neighbor, row, pos) == woman)
                {
                    res--;
                    break;
                }
                else man = woman_neighbor;
            }
        }
        return res;
    }
};

总结

其实很多题都是一个思路,不需要把他要强安上什么算法,什么数据结构的。根据这么多刷题的经验,对于题目应该用什么样的大体方法来解决,都有一个比较清晰的概念了,所以就直接去想,遇到问题也多坚持一下就好了。
刷题的进步还是很明显的,最起码这种感觉不会骗人。但是有一个隐患,就是自己不怎么在意自己算法的速度,这点还是要注意一下的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值