【发布时间】:2016-12-02 03:57:25
【问题描述】:
我看到的问题如下,有人对此有所了解吗? http://judgecode.com/problems/1011
给定从 0 到 n - 1 的整数排列,对它们进行排序很容易。但是如果每次只能交换一对整数呢?
请计算最小交换次数
【问题讨论】:
我看到的问题如下,有人对此有所了解吗? http://judgecode.com/problems/1011
给定从 0 到 n - 1 的整数排列,对它们进行排序很容易。但是如果每次只能交换一对整数呢?
请计算最小交换次数
【问题讨论】:
一种经典算法似乎是置换循环 (https://en.wikipedia.org/wiki/Cycle_notation#Cycle_notation)。所需的交换次数等于元素总数减去循环次数。
例如:
1 2 3 4 5
2 5 4 3 1
Start with 1 and follow the cycle:
1 down to 2, 2 down to 5, 5 down to 1.
1 -> 2 -> 5 -> 1
3 -> 4 -> 3
我们需要将索引 1 与 5 交换,然后将索引 5 与 2 交换;以及索引 3 和索引 4。总共 3 个交换或n - 2。我们将循环数减去n,因为循环元素加在一起的总数为n,并且每个循环表示一个交换小于其中的元素数。
【讨论】:
这是C 中针对上述问题的一个简单实现。该算法类似于用户 גלעד ברקן:
a[]的每个元素的位置存储在b[]中。所以,b[a[i]] = i
a[]。i,检查a[i] 是否等于i。如果yes,则继续迭代。no,那么是时候交换了。仔细查看代码中的逻辑以了解交换是如何发生的。 这是最重要的一步,因为数组a[] 和b[] 都需要修改。 增加交换的count。 这里是实现:
long long sortWithSwap(int n, int *a) {
int *b = (int*)malloc(sizeof(int)*n); //create a temporary array keeping track of the position of every element
int i,tmp,t,valai,posi;
for(i=0;i<n;i++){
b[a[i]] = i;
}
long long ans = 0;
for(i=0;i<n;i++){
if(a[i]!=i){
valai = a[i];
posi = b[i];
a[b[i]] = a[i];
a[i] = i;
b[i] = i;
b[valai] = posi;
ans++;
}
}
return ans;
}
【讨论】:
解决这个问题的本质在于以下观察
1.数组中的元素不重复
2.元素的范围是从0到n-1,其中n是数组的大小。
方法
在您了解了解决问题的方法之后,您可以在线性时间内解决它。
想象一下,在对所有条目进行排序后,数组会是什么样子?
对于所有条目,它看起来像 arr[i] == i。这有说服力吗?
首先创建一个名为 FIX 的 bool 数组,其中 FIX[i] == true 如果第 i 个位置是固定的,则初始化这个数组为 false
开始检查原始数组是否匹配 arr[i] == i,直到这个条件成立,一切都好。在继续遍历数组的同时,还要更新 FIX[i] = true。
当你发现 arr[i] != i
你需要做一些事情时, arr[i] 必须有一些值 x这样 x > i,我们如何保证?保证来自数组中的元素不重复这一事实,因此如果数组排序到索引 i 则意味着数组中位置 i 处的元素不能来自左侧,而是来自右侧。
现在值 x 本质上是在说某个索引,为什么会这样,因为数组只有从 0 开始的 n-1 的元素,并且在排序后的数组中,数组的每个元素 i 都必须位于位置 i。
arr[i] == x 的意思是,不仅元素 i 不在它的正确位置,而且元素 x 从它的位置丢失。
现在要修复第 i 个位置,您需要查看第 x 个位置,因为可能第 x 个位置包含 i,然后您将交换索引 i 和 x 处的元素,并完成工作。
但是等等,索引没有必要x 将持有 i (您只需 1 次交换即可完成这些位置的修复)。相反,索引 x 有可能保存值 y,它再次大于 i,因为数组只排序到位置 i。
现在在你可以固定位置 i 之前,你需要固定 x,为什么?我们稍后会看到。
因此,现在您再次尝试修复位置 x,然后类似地您将尝试修复,直到您在某个位置看不到元素 i 时。
时尚是跟随 arr[i] 的链接,直到你在某个索引处点击元素 i。
以这种方式跟随,保证你一定会在某个位置打到i。为什么 ?试着证明它,做一些例子,你会感觉到的
现在您将开始修复您在从索引 i 到该索引(比如 j)的路径中看到的所有索引。现在你看到的是你所遵循的路径是一个循环路径,对于每个索引 i,arr[i] 在它的前一个索引处被撕裂(从你到达这里的索引),一旦你看到你可以修复索引,并将 FIX 数组中的所有索引标记为真。现在继续数组的下一个索引并做同样的事情,直到整个数组被修复..
这是完整的想法,但只能说不。对于交换,您会发现,一旦找到 n 个元素的循环,就需要 n 个交换,然后修复数组,然后再次继续。所以这就是你将如何计算没有。掉期。
如果您对该方法有任何疑问,请告诉我。
您也可以要求 C/C++ 代码帮助。
乐于助人 :-)
【讨论】: