1067 Sort with Swap(0, i) (25 分)

这篇博客探讨了如何在仅允许一次 Swap(0, *) 操作的情况下,找出对 N 个非负整数的给定排列进行排序所需的最少交换次数。通过预处理0的位置和使用 map 和 set 数据结构,作者分享了高效的算法优化策略。
摘要由CSDN通过智能技术生成

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 (≤10​5​​) 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

题目大意:

给一组无序的数,通过多次的swap(0,*)变换,将该组数变为递增序列。

思路分析:

首先约定,若数字 i 位于下标为 i 的位置上,则说 i 位于正确的位置;

经过尝试,最终分为两种情况:

① 0不在正确的位置上,则0与0所在位置的数字交换,即swap(a[ 0所在的位置],a[ 0所在的位置的数字所在的位置]);

② 0在正确的位置上,则0与任一未处于正确位置的数字交换。

基于以上两种结论,我写出了第一版暴力的代码,但是超时了,然后就是基于以上两个结论的时间优化。

① 0的位置可以在输入的时候标记,然后随着每次交换的进行,不断记录;另一个下标则使用 map<数字,该数字所在位置的下标>,可以直接索引到某一数字所在的位置。

② 这个结论要重点优化找到未处于正确位置的数字的过程,如果挨个遍历的话也会超时,可以使用set记录当前位置上数字不正确的所有位置,每次需要的时候就调用set的第一个数。

优化后的代码如下:

#include<bits/stdc++.h>
#define ll long long
#define mod 1000000007
#define maxn 100000+10
using namespace std;

int a[maxn];
map<int,int>m;
set<int>s;
int main()
{
    int n;cin>>n;
    int ans=0;
    int k;
    for(int i=0;i<n;i++){
        scanf("%d",&a[i]);
        m[a[i]]=i;
        if(a[i]==0)k=i;
        if(a[i]!=i&&i!=0){
            s.insert(i);
        }
    }
    while(1){
        if(k!=0){
            swap(a[m[k]],a[k]);
            ans++;
            s.erase(s.find(k));
            int t=m[k];m[k]=k;k=t;
        }
        else { 
            if(s.empty())break;
            else{
                set<int>::iterator it=s.begin();
                swap(a[*it],a[0]);
                ans++;
                m[a[0]]=0;
                m[0]=*it;
                k=*it;
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值