题目描述
【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;
}
};
结果: