leetcode:765. 情侣牵手

题目来源

题目描述

在这里插入图片描述

class Solution {
public:
    int minSwapsCouples(vector<int>& row) {

    }
};

题目解析

题意:

  • 给出一个数组,数组里面两两相邻的元素代表一对情侣。情侣编号是从0开始的::0 和 1 是情侣,2 和 3 是情侣……这些情侣坐在⼀排,但是并⾮成对坐着⼀起的,问如何⽤最⼩的次数交换座位以后,情侣能两两坐在⼀起

贪心

举个例子:[3 1 4 0 2 5]

我们来两个两个的看数字。

  • 前两个数是3和1,不是情侣,3的伴侣应该是2,怎么办呢?那么我们找到2的位置,将2和1交换位置。
    • [3 2 4 0 1 5]
  • 再取两个数字4和0,不是情侣,4的伴侣应该是5,怎么办呢?那么我们找到5的位置,将5与0交换位置
    • [3 2 4 5 1 0]
  • 最后两个数字1和0,不用动,因为前面都成对,最后两个数字一定成对

怎么写?

class Solution {
public:
    int minSwapsCouples(vector<int>& row) {
        int result = 0;
        int n = row.size();
        for (int i = 0; i < n; i += 2) {
            if (row[i] == (row[i + 1] ^ 1)) continue; // 运用异或运算判断是不是一对情侣
            for (int j = i + 1; j < n; j++) { // 如果不是,再进行搜寻
                if ((row[j] ^ 1) == row[i]) { // 找到了另一半
                    swap(row[i + 1], row[j]); // 交换座位
                    result++; // 交换次数+1
                    break;
                }
            }
        }
        return result; 
    }
};

并查集

这道题说到底是一个群组问题,所以可以用并查集来搞。

举个例子:[3 1 4 0 2 5]

刚开始的群组关系是:

  • 群组0:0,1
  • 群组1:2,3
  • 群组2:4,5

取出前两个数字3和1,其群组号分别为1和0,带入 find 函数返回不同值,则此时将群组0和群组1链接起来,变成一个群组,则此时只有两个群组了,res 自增1,变为了1。

  • 群组0 & 1:0,1,2,3
  • 群组2:4,5

此时取出4和0,其群组号分别为2和0,带入 find 函数返回不同值,则此时将群组 0&1 和群组2链接起来,变成一个超大群组,res 自增1,变为了2。

  • 群组0 & 1 & 2:0,1,2,3,4,5

此时取出最后两个数2和5,其群组号分别为1和2,因为此时都是一个大组内的了,带入 find 函数返回相同的值,不做任何处理。最终交换的步数就是 res 值,为2。

又如果一对情侣恰好坐在了一起,并且坐在了成组的座位上,其中一个下标一定是偶数,另一个一定是奇数,并且「偶数的值 + 1 = 奇数的值」。例如编号数对 [2, 3]、[9, 8],这些数对的特点是除以 22(下取整)得到的数相等。

代码如下:

class Solution {
    class UnionFind{
    private:
        std::vector<int> parent; // parent[i] = k : i的父亲是k
        std::vector<int> size;   // size[i] = k : 如果i是代表节点,size[i]才有意义( i所在的集合大小是多少),否则无意义
        std::vector<int> help;  //  辅助结构
        int cnt;               //一共有多少个集合
    public:
        explicit UnionFind(int n){
            cnt = n;
            parent.resize(n);
            size.resize(n);
            help.resize(n);
            for (int i = 0; i < n; ++i) {
                parent[i] = i;
                size[i] = 1;
            }
        }

        // 返回i的代表节点
        // 这个过程要做路径压缩
        int findRoot(int i){
            int hi = 0;
            while (i != parent[i]){
                help[hi++] = parent[i];
                i = parent[i];
            }
            for (hi--; hi >= 0; --hi) {
                parent[help[hi]] = i;
            }
            return i;
        }

        void merge(int i, int j){
            int f1 = findRoot(i);
            int f2 = findRoot(j);
            if(f1 != f2){
                if(size[f1] >= size[f2]){
                    parent[f2] = f1;
                    size[f1] = size[f1] + size[f2];
                }else{
                    parent[f1] = f2;
                    size[f2] = size[f2] + size[f1];
                }
                --cnt;
            }
        }

        int counts() const{
            return cnt;
        }
    };
public:
    int minSwapsCouples(vector<int>& row) {
        int len = row.size();
        int N = len / 2;
        UnionFind unionFind(N);
        for (int i = 0; i < len; i += 2) {
            unionFind.merge(row[i] / 2, row[i + 1] / 2);
        }
        return N - unionFind.counts();
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值