10-排序6 Sort with Swap(0, i)

209 阅读1分钟

Problem Description

Given any permutation of the numbers {0, 1, 2,..., N−1}, it is easy to sort them in increasing order. But what if Swap(0, *) is the ONLY operation that is allowed to use? For example, to sort {4, 0, 2, 1, 3} we may apply the swap operations in the following way:

Swap(0, 1) => {4, 1, 2, 0, 3}
Swap(0, 3) => {4, 1, 2, 3, 0}
Swap(0, 4) => {0, 1, 2, 3, 4}

Now you are asked to find the minimum number of swaps need to sort the given permutation of the first N nonnegative integers.

Input Specification

Each input file contains one test case, which gives a positive N (≤105) followed by a permutation sequence of {0, 1, ..., N−1}. All the numbers in a line are separated by a space.

Output Specification

For each case, simply print in a line the minimum number of swaps need to sort the given permutation.

Sample Input

10
3 5 7 2 6 4 9 0 8 1

Sample Output

9

Solution

题意理解:给定 0 到 N - 1 的一个排序,要求仅使用 0 和另一个数交换的方式来达到非递减排序,求最少要交换的次数。

很容易想到一个模拟的方法根据题意写出排序算法并计算交换次数即可,但非常显然地超时。

![[../../assets/Pasted image 20220826175546.png]]

第一次尝试(TLE):

#include <stdio.h>
#include <stdlib.h>

void Swap(int *a, int *b)
{
    int tmp;
    tmp = *a;
    *a = *b;
    *b = tmp;
}

int IsOrdered(int A[], int N)
{
    int i, flag = 1;
    for (i = 0; i < N && flag; i++) {
        if (A[i] != i) flag = 0;
    }
    return flag;
}

int Find(int A[], int N, int num)
{
    int i;
    for (i = 0; i < N; i++)
        if (A[i] == num)
            return i;
}

int FindNew(int A[], int N)
{
    int i;
    for (i = 0; i < N; i++)
        if (A[i] != i) return i;
}

int SwapSort(int A[], int N)
{
    int i, position0, times = 0;
    while (!IsOrdered(A, N)) {
        position0 = Find(A, N, 0);
        if (A[0] != 0) {
            Swap(&A[position0], &A[Find(A, N, position0)]);
        } else {
            Swap(&A[position0], &A[FindNew(A, N)]);
        }
        times++;
    }
    return times;
}

int main()
{
    int i, N, A[100000];
    scanf("%d", &N);
    for (i = 0; i < N; i++) scanf("%d", &A[i]);
    printf("%d", SwapSort(A, N));
    return 0;
}

第二次尝试(AC):

由表排序的思想可知,每次 Swap( 0 , * ) 最后都会成一个环。

  • 当环内只有一个元素时,已经落在正确位置上,不交换
  • 当环内含有 0 时,0 要和环内剩下的其他元素交换,交换次数就等于环内元素减一
  • 当环内不含有 0 时,先将 0 交换进入环内,再与每个环内元素交换一次,交换次数等于环内元素加一

不需要真的去 Swap( 0 , * ) ,只需要用表排序将每个环内交换的次数累加起来即为答案。

程序框架:

  • 读入 A[i]
  • 构造 T[i] :存放的是 iA 中的下标
  • 计算结果
  • 输出结果

表排序详解:

  • 外层循环遍历数组下标,进入循环后,如果 A[i] != i 就进入环内排序
    • 如果该环内没有 0 ,初始化 cnt 为 1
    • 如果环内有 0 ,初始化 cnt 为 1
    • j = i 进入内循环
      • 循环结束条件是 T[j] != j ,最后把刚进入环内的那个元素放在当前 j 的上一个位置 p
      • 进入循环,A[j] = A[T[j]]; ,把正确的元素落上位置
      • 记下当前所作操作的下标为 p
      • 更新 jT[j]
      • T[p] = p;
      • 交换一次计数器加一
    • 最后退出循环落好位置后加上一次环的交换次数
  • 全部遍历完毕后输出总交换次数
#include <stdio.h>
#include <stdlib.h>

int SortWithSwap(int A[], int N, int T[])
{
    int i, j, p, tmp, sum, cnt;
    
    sum = 0;
    for (i = 0; i < N; i++) {
        if (A[i] != i) {
            if (A[T[i]] == 0) cnt = -1;
            else cnt = 1;
            j = i;
            tmp = A[j];
            while (T[j] != j) {
                A[j] = A[T[j]];
                p = j;
                j = T[j];
                T[p] = p;
                cnt++;
            }
            A[p] = tmp;
            sum += cnt;
        }
    }

    return sum;
}

int main()
{
    int i, N, A[100000], T[100000];
    scanf("%d", &N);
    for (i = 0; i < N; i++) scanf("%d", &A[i]);
    for (i = 0; i < N; i++) T[A[i]] = i;
    printf("%d", SortWithSwap(A, N, T));
    return 0;
}