【leetcode】765. 情侣牵手( Couples Holding Hands )


题目描述

【leetcode】765. 情侣牵手( Couples Holding Hands )

N 对情侣坐在连续排列的 2N 个座位上,想要牵到对方的手。 计算最少交换座位的次数,以便每对情侣可以并肩坐在一起。 一次交换可选择任意两人,让他们站起来交换座位。

人和座位用 0 到 2N-1 的整数表示,情侣们按顺序编号,第一对是 (0, 1),第二对是 (2, 3),以此类推,最后一对是 (2N-2, 2N-1)。

这些情侣的初始座位 row[i] 是由最初始坐在第 i 个座位上的人决定的。

示例 1:

输入: row = [0, 2, 1, 3]
输出: 1
解释: 我们只需要交换row[1]和row[2]的位置即可。

示例 2:

输入: row = [3, 2, 0, 1]
输出: 0
解释: 无需交换座位,所有的情侣都已经可以手牵手了。

说明:

  • len(row) 是偶数且数值在 [4, 60]范围内。
  • 可以保证row 是序列 0…len(row)-1 的一个全排列。

第一次解答

思路
贪心法。
比较难容易让人困惑的是下面这两个,到底要移动多少次,要是没想明白的话,贪心也用不成。注意,贪心每走一步,面对的情况都需要更新,而不是说贪心每走一步,面对的情况还是之前的。
第一个例子,走之前发现有4个数(3,1,7,5)要交换,按理说要交换3次,可实际这里只需要交换两次,因为交换完第一次后,情况更新为只有2个数(7,5)要交换。
对于第二个例子,就需要交换3次。
示例一:[0,3,2,1,4,7,6,5]
示例二:[0,7,2,1,4,3,6,5]

tese case:
[0,2,1,3]
[3, 2, 0, 1]
[0,3,2,1,4,7,6,5]
[0,2,4,6,7,1,3,5]
[28,4,37,54,35,41,43,42,45,38,19,51,49,17,47,25,12,53,57,20,2,1,9,27,31,55,32,48,59,15,14,8,3,7,58,23,10,52,22,30,6,21,24,16,46,5,33,56,18,50,39,34,29,36,26,40,44,0,11,13]

代码:

class Solution {
    
    
public:
    int minSwapsCouples(vector<int>& row) {
    
    
        // 记录每个元素的索引
        vector<int> from(row.size());
        for(int i=0; i<row.size(); ++i){
    
    
            from[row[i]] = i;
        }

        int count = 0; // 记录操作次数
        for(int i=0; i+1<row.size(); i+=2){
    
    
            // 若不配对
            if(row[i]/2 != row[i+1]/2){
    
    
                count++;
                int other_index = row[i]&0x01 ? from[row[i]-1] : from[row[i]+1];
                swap(row[other_index], row[i+1]);
                swap(from[row[other_index]], from[row[i+1]]);
            }
        }
        return count;
    }
};

结果:
截图

第二次解答

思路:
参考leetcode官方题解里的方法二: 循环搜索 【通过】,这里已经讲得很好了大家点进链接看就行。我这里主要是补一张图,对于下图1,大家应该没什么疑惑;但是对于下图2有的人可能会困惑,我一开始也以为,前两组情侣一对调后会一下子满足两对情侣,咋一看之下好像新增加了2个联通分量,其实不然,还是只增加了一个联通分量。

在这里插入图片描述 在这里插入图片描述
图1 图2

根据上面分析,我们找出联通分量数count,然后结果就是N-count了,至于找联通分量个数,可以dfs,也可以用并查集,这里用并查集。
代码:

// 思路:
// 并查集。2N个座位可以分成N个双人沙发。若所有情侣都成功牵手,则有N个双人沙发。
// 独立沙发:沙发上坐着牵手的情侣
// 联通沙发:沙发上坐着的不是一对情侣,另一半在其他沙发上。
// 联通沙发为2时,交换1次即可
// 联通沙发为3时,交换2次即可
// 联通沙发为4时,交换3次即可,注意,下图不是1个4联通沙发,而是2个2联通的沙发:
// 0,3沙发与2,1沙发2联通,4,7沙发与6,5沙发2联通,所以是2个2联通沙发,所以要操作2个1次,也就是一共要操作2次。
// [0,3,2,1,4,7,6,5]
class Solution {
    
    
public:
    int count;
    vector<int> parents;
    int MyFind(int a){
    
    
        while(a != parents[a]){
    
    
            parents[a] = parents[parents[a]];
            a = parents[a];
        }
        return a;
    }
    void MyUnion(int a, int b){
    
    
        int p1 = MyFind(a);
        int p2 = MyFind(b);
        if(p1 == p2) return;
        parents[p1] = p2;
        count--;
    }
    int minSwapsCouples(vector<int>& row) {
    
    
        int N = row.size() / 2; // 牵手成功后的沙发数量
        count = N;
        parents.clear();
        parents.resize(N);
        for(int i=0; i<N; ++i){
    
    
            parents[i] = i;
        }
        for(int i=0; i+1<row.size(); i+=2){
    
    
            if(row[i]/2 != row[i+1]/2){
    
    
                MyUnion(row[i]/2, row[i+1]/2);
            }
        }

        return N-count;
    }
};

结果:
在这里插入图片描述

相关/参考链接

猜你喜欢

转载自blog.csdn.net/a435262767/article/details/105430636