同一番号のない整数シーケンスを(一般性を失うことなく、そのシーケンスが1,2,...,n
の順列であると仮定して)自然の昇順(つまり1,2,...,n
)にソートする作業をしています。要素を直接交換することを考えていました(要素の位置に関係なく、つまり、2つの要素に対して有効な交換です)。最小限の交換で実行できます(次の解決策も考えられます)。
1つまたは両方の要素を正しい位置に入れ替える必要があるという制約で2つの要素を入れ替えます。すべての要素が正しい位置に配置されるまで。
しかし、上記のソリューションが最適であるかどうかを数学的に証明する方法がわかりません。誰でも手伝ってくれる?
graph-theory でこれを証明することができました。そのタグを:)に追加したいかもしれません
n
頂点を持つグラフを作成します。ノードn_i
からn_j
へのエッジを作成します。位置i
の要素が正しい順序でj
の位置にある必要があります。これで、いくつかの非交差サイクルで構成されるグラフができます。グラフを正しく順序付けるために必要なスワップの最小数は
M = sum (c in cycles) size(c) - 1
少し時間をかけて、それを納得してください... 2つのアイテムがサイクル内にある場合、1つのスワップでそれらを処理できます。 3つのアイテムがサイクルにある場合、ペアを交換して1つを正しい場所に配置し、2サイクルを残すなどできます。n
アイテムがサイクルにある場合は、n-1
が必要ですスワップ。 (これは、すぐ隣の人とスワップしない場合でも常に当てはまります。)
これで、アルゴリズムが最適である理由を確認できるようになります。入れ替えを行い、少なくとも1つのアイテムが正しい位置にある場合、M
の値は常に1ずつ減少します。長さn
のサイクルの場合、要素をその隣人が占める正しいスポット。これで、正しく順序付けられた要素と長さn-1
のサイクルができました。
M
はスワップの最小数であり、アルゴリズムはM
をスワップごとに常に1ずつ減らすため、最適である必要があります。
えーと、すべての循環計算は頭の中で維持するのが非常に困難です。覚える方がはるかに簡単な方法があります。
まず、手作業でサンプルケースを投げましょう。
swap(0,2);swap(0,3)
はswap(2,3);swap(0,2)
[.____と同じです。]swap(0, 1)
=> [(3、2)、(1、1)、(2、3)、(4、4)、(5 、5)、(6、6)、(0、7)]swap(0, 3)
=> [(4、4)、(1、1)、(2、3)、(3、2)、(5 、5)、(6、6)、(0、7)]swap(0, 4)
=> [(5、5)、(1、1)、(2、3)、(3、2)、(4、4)、(6、6)、(0、7)]swap(0, 5)
=> [(6、6)、(1、1)、(2、3)、(3、2)、(4、4)、(5、5)、(0、7)]swap(0, 6)
=> [(0、7)、(1、1)、(2、3)、(3、2)、(4、4)、(5、 5)、(6、6)]つまり意味的には、要素を並べ替え、配置されていない左端のアイテムをスワップして、要素を初期状態にする方法を見つけます。
Pythonアルゴリズムは次のように単純です。
def swap(arr, i, j):
arr[i], arr[j] = arr[j], arr[i]
def minimum_swaps(arr):
annotated = [*enumerate(arr)]
annotated.sort(key = lambda it: it[1])
count = 0
i = 0
while i < len(arr):
if annotated[i][0] == i:
i += 1
continue
swap(annotated, i, annotated[i][0])
count += 1
return count
したがって、訪問したノードを記憶したり、サイクルの長さを計算したりする必要はありません。
参考までに、配列をソートするのに必要な最小数のスワップを生成するために、私が書いたアルゴリズムを以下に示します。 @Andrew Maoによって記述されたサイクルを検出します。
/**
* Finds the minimum number of swaps to sort given array in increasing order.
* @param ar array of <strong>non-negative distinct</strong> integers.
* input array will be overwritten during the call!
* @return min no of swaps
*/
public int findMinSwapsToSort(int[] ar) {
int n = ar.length;
Map<Integer, Integer> m = new HashMap<>();
for (int i = 0; i < n; i++) {
m.put(ar[i], i);
}
Arrays.sort(ar);
for (int i = 0; i < n; i++) {
ar[i] = m.get(ar[i]);
}
m = null;
int swaps = 0;
for (int i = 0; i < n; i++) {
int val = ar[i];
if (val < 0) continue;
while (val != i) {
int new_val = ar[val];
ar[val] = -1;
val = new_val;
swaps++;
}
ar[i] = -1;
}
return swaps;
}
実際の要素を交換する必要はありません。正しいインデックス(Cycle)にない要素の数を見つけるだけです。最小スワップはサイクル-1になります。これがコードです...
static int minimumSwaps(int[] arr) {
int swap=0;
boolean visited[]=new boolean[arr.length];
for(int i=0;i<arr.length;i++){
int j=i,cycle=0;
while(!visited[j]){
visited[j]=true;
j=arr[j]-1;
cycle++;
}
if(cycle!=0)
swap+=cycle-1;
}
return swap;
}
//ゼロから始まるシーケンスのみを処理するとします
function minimumSwaps(arr) {
var len = arr.length
var visitedarr = []
var i, start, j, swap = 0
for (i = 0; i < len; i++) {
if (!visitedarr[i]) {
start = j = i
var cycleNode = 1
while (arr[j] != start) {
j = arr[j]
visitedarr[j] = true
cycleNode++
}
swap += cycleNode - 1
}
}
return swap
}
@Archibald、私はあなたの解決策が好きで、配列をソートすることが最も簡単な解決策であるという私の最初の仮定でしたが、私がそれを吹き替えたように、逆トラバースの労力を経る必要があるとは思いません、すなわち配列を列挙してから並べ替え、列挙型のスワップを計算します。
配列の各要素から1を減算し、そのリストをソートするために必要なスワップを計算する方が簡単だと思います
ここに私の微調整/解決策があります:
def swap(arr, i, j):
tmp = arr[i]
arr[i] = arr[j]
arr[j] = tmp
def minimum_swaps(arr):
a = [x - 1 for x in arr]
swaps = 0
i = 0
while i < len(a):
if a[i] == i:
i += 1
continue
swap(a, i, a[i])
swaps += 1
return swaps
最適性を証明することに関しては、@ araxが良い点だと思います。
Swift 4バージョン:
func minimumSwaps(arr: [Int]) -> Int {
struct Pair {
let index: Int
let value: Int
}
var positions = arr.enumerated().map { Pair(index: $0, value: $1) }
positions.sort { $0.value < $1.value }
var indexes = positions.map { $0.index }
var swaps = 0
for i in 0 ..< indexes.count {
var val = indexes[i]
if val < 0 {
continue // Already visited.
}
while val != i {
let new_val = indexes[val]
indexes[val] = -1
val = new_val
swaps += 1
}
indexes[i] = -1
}
return swaps
}
配列のカウントが1で始まる場合
function minimumSwaps(arr) {
var len = arr.length
var visitedarr = []
var i, start, j, swap = 0
for (i = 0; i < len; i++) {
if (!visitedarr[i]) {
start = j = i
var cycleNode = 1
while (arr[j] != start + 1) {
j = arr[j] - 1
visitedarr[j] = true
cycleNode++
}
swap += cycleNode - 1
}
}
return swap
}
それ以外の場合、0で始まる入力用
function minimumSwaps(arr) {
var len = arr.length
var visitedarr = []
var i, start, j, swap = 0
for (i = 0; i < len; i++) {
if (!visitedarr[i]) {
start = j = i
var cycleNode = 1
while (arr[j] != start) {
j = arr[j]
visitedarr[j] = true
cycleNode++
}
swap += cycleNode - 1
}
}
return swap
}
現在のHackerEarth入力に対してDarshan Puttaswamyコードを拡張するだけ
@bekceによる解決策。 C#を使用する場合、変更された配列ar
をセットアップする初期コードは、次のように簡潔に表現できます。
var origIndexes = Enumerable.Range(0, n).ToArray();
Array.Sort(ar, origIndexes);
次に、残りのコードでorigIndexes
の代わりにar
を使用します。
Pythonコード
A = [4,3,2,1]
count = 0
for i in range (len(A)):
min_idx = i
for j in range (i+1,len(A)):
if A[min_idx] > A[j]:
min_idx = j
if min_idx > i:
A[i],A[min_idx] = A[min_idx],A[i]
count = count + 1
print "Swap required : %d" %count
Java(およびテスト))のプリミティブ型の整数の実装。
import Java.util.Arrays;
public class MinSwaps {
public static int computate(int[] unordered) {
int size = unordered.length;
int[] ordered = order(unordered);
int[] realPositions = realPositions(ordered, unordered);
boolean[] touchs = new boolean[size];
Arrays.fill(touchs, false);
int i;
int landing;
int swaps = 0;
for(i = 0; i < size; i++) {
if(!touchs[i]) {
landing = realPositions[i];
while(!touchs[landing]) {
touchs[landing] = true;
landing = realPositions[landing];
if(!touchs[landing]) { swaps++; }
}
}
}
return swaps;
}
private static int[] realPositions(int[] ordered, int[] unordered) {
int i;
int[] positions = new int[unordered.length];
for(i = 0; i < unordered.length; i++) {
positions[i] = position(ordered, unordered[i]);
}
return positions;
}
private static int position(int[] ordered, int value) {
int i;
for(i = 0; i < ordered.length; i++) {
if(ordered[i] == value) {
return i;
}
}
return -1;
}
private static int[] order(int[] unordered) {
int[] ordered = unordered.clone();
Arrays.sort(ordered);
return ordered;
}
}
テスト
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class MinimumSwapsSpec {
@Test
public void example() {
// setup
int[] unordered = new int[] { 40, 23, 1, 7, 52, 31 };
// run
int minSwaps = MinSwaps.computate(unordered);
// verify
assertEquals(5, minSwaps);
}
@Test
public void example2() {
// setup
int[] unordered = new int[] { 4, 3, 2, 1 };
// run
int minSwaps = MinSwaps.computate(unordered);
// verify
assertEquals(2, minSwaps);
}
@Test
public void example3() {
// setup
int[] unordered = new int[] {1, 5, 4, 3, 2};
// run
int minSwaps = MinSwaps.computate(unordered);
// verify
assertEquals(2, minSwaps);
}
}
Swift 4.2:
func minimumSwaps(arr: [Int]) -> Int {
let sortedValueIdx = arr.sorted().enumerated()
.reduce(into: [Int: Int](), { $0[$1.element] = $1.offset })
var checked = Array(repeating: false, count: arr.count)
var swaps = 0
for idx in 0 ..< arr.count {
if checked[idx] { continue }
var edges = 1
var cursorIdx = idx
while true {
let cursorEl = arr[cursorIdx]
let targetIdx = sortedValueIdx[cursorEl]!
if targetIdx == idx {
break
} else {
cursorIdx = targetIdx
edges += 1
}
checked[targetIdx] = true
}
swaps += edges - 1
}
return swaps
}
ここにJavaの解決策があります。@ Archibaldがすでに説明したものです。
static int minimumSwaps(int[] arr){
int swaps = 0;
int[] arrCopy = arr.clone();
HashMap<Integer, Integer> originalPositionMap
= new HashMap<>();
for(int i = 0 ; i < arr.length ; i++){
originalPositionMap.put(arr[i], i);
}
Arrays.sort(arr);
for(int i = 0 ; i < arr.length ; i++){
while(arr[i] != arrCopy[i]){
//swap
int temp = arr[i];
arr[i] = arr[originalPositionMap.get(temp)];
arr[originalPositionMap.get(temp)] = temp;
swaps += 1;
}
}
return swaps;
}
これは、C++のサンプルコードで、(1,2,3,4,5,.......n-2,n-1,n)
のシーケンスの順列を並べ替えるための最小数のスワップを見つけます。
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,i,j,k,num = 0;
cin >> n;
int arr[n+1];
for(i = 1;i <= n;++i)cin >> arr[i];
for(i = 1;i <= n;++i)
{
if(i != arr[i])// condition to check if an element is in a cycle r nt
{
j = arr[i];
arr[i] = 0;
while(j != 0)// Here i am traversing a cycle as mentioned in
{ // first answer
k = arr[j];
arr[j] = j;
j = k;
num++;// reducing cycle by one node each time
}
num--;
}
}
for(i = 1;i <= n;++i)cout << arr[i] << " ";cout << endl;
cout << num << endl;
return 0;
}