使序列有序的最少交换次数-任意交换元素位置

题意: 给定一个包含1-n的数列,通过交换任意两个元素给数列重新排序。求最少需要多少次交换,能把数组排成按1-n递增的顺序,(数组中的元素互不重复)。

举例: 原数列为2,4,3,6,5,7;第一次交换(3,4)得2,3,4,6,5,7;第二次交换(5,6)得2,3,4,5,6,7。完成排序需2次。

思想: 构造“循环节”,找原位置与排序后位置不一致的形成交换环。元素2位置无需变动自形成一个环;元素4位置需变动(应在元素3位置),因此将元素4与经排序后要放入的位置处元素(‘3’)形成一个环;同理6,5形成一个环;7自形成一个环。一共四个环。我们都知道在一个环内将其元素按有序排列所要的交换次数为:环内元素个数-1。因此求解此题次数n = 数列元素个数-环数。

证明:假设有m个环,完成任务总的交换次数 = 环1元素个数-1+环2元素个数-1+....+环m元素个数-1  = (环1元素个数+环2元素个数+.....+环m元素个数 = 数列元素总个数)- (1+1+....+1 = m)。

代码:

#include<iostream>
#include<cstdio>
#include<map>
#include<algorithm>
using namespace std;
int a[100], b[100],flag[100];
int main(){
    int n, circle = 0;
    map<int, int> mp;
    cin >> n;
    for(int i = 1; i <= n; i++){
        scanf("%d",&a[i]);
        b[i] = a[i];
        mp[a[i]] = i;
    }
    sort(b+1,b+1+n);
    for(int j = 1; j <= n; j++){
        if(!flag[j]){
            int tp = j;
            while(!flag[tp]){
                flag[tp] = 1;
                tp = mp[b[tp]];
            }
            circle++;//循环节大小 
        }
    }
    cout << n - circle;//最小交换次数 
    return 0;
}
#include<bits/stdc++.h>
using namespace std;
/**
 *  交换任意两数的本质是改变了元素位置,
 *  故建立元素与其目标状态应放置位置的映射关系
 */
int getMinSwaps(vector<int> v)
{
    vector<int> v1(v);   //将A内元素复制到B。
    sort(v1.begin(), v1.end());
    map<int,int> m;
    int len = v.size();
    for (int i = 0; i < len; i++)
    {
        m[v1[i]] = i;    //  建立每个元素与其应放位置的映射关系
    }
    int loops = 0;      //  循环节个数
    vector<bool> flag(len, false); //初始化
    //找出循环节的个数
    for (int i = 0; i < len; i++)
    {
        if (!flag[i])
        {
            int j = i;
            while (!flag[j])     //对环处理
            {
                flag[j] = true;
                j = m[v[j]];    //原序列中j位置的元素在有序序列中的位置
            }
            loops++;
        }
    }
    return len - loops;
}
vector<int> v;
int main()
{
    int n,k;
    cin>>n;
    while(n--){
        cin>>k;
        v.push_back(k);
    }
    int num = getMinSwaps(v);
    cout<<"交换次数:"<<num<<endl;
    return 0;
}

 

 

然后看例题

现在有两个长度为n的排列p和s。要求通过交换使得p变成s。交换 pipi 和 pjpj 的代价是|i-j|。要求使用最少的代价让p变成s。

输入

单组测试数据。
第一行有一个整数n (1≤n≤200000),表示排列的长度。
第二行有n个范围是1到n的整数,表示排列p。每个整数只出现一次。
第三行有n个范围是1到n的整数,表示排列s。每个整数只出现一次。

输出

输出一个整数,表示从排列p变到s最少要多少代价。

输入样例

样例输入1
4
4 2 1 3
3 2 4 1

输出样例

样例输出1
3

这个题我是看了上面的题解有一个想法,就是如果它形成一个换那么它走的肯定是2*权值
#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll; 
const int maxn=2e6+100;
ll a[maxn],b[maxn],c[maxn],d[maxn],flag[maxn];
ll mp[maxn];
int n;
int main(){
    cin>>n;
    ll ans=0;
    for(int i=1;i<=n;i++){
        cin>>c[i];
    }
    for(int i=1;i<=n;i++){
        int x;
        cin>>x;
        d[x]=i;
    }
    for(int i=1;i<=n;i++){
        a[i]=d[c[i]];
    }
    for(int i=1;i<=n;i++){
        b[i] = a[i];
        mp[a[i]] = i;
    }
    sort(b+1,b+1+n);
    for(int j = 1; j <= n; j++){
        if(!flag[j]){
            int tp = j;
            ll sum=0;
            while(!flag[tp]){
                flag[tp] = 1;
                sum+=abs(tp-mp[b[tp]]);
                tp = mp[b[tp]]; 
            }
            ans+=sum/2;
        }
    }
    cout<<ans<<endl;
}

其实这个题是可以更简单的

#include<set>  
#include<map>         
#include<stack>                
#include<queue>  
#include<vector>        
#include<string>      
#include<math.h> 
#include<time.h>  
#include<stdio.h>                
#include<iostream>                
#include<string.h>                
#include<stdlib.h>        
#include<algorithm>       
#include<functional>        
using namespace std;
typedef long long ll;
#define inf 1000000000           
#define mod 1000000007                
#define maxn  200005    
#define PI 3.1415926  
#define lowbit(x) (x&-x)     
#define eps 1e-9
ll a[maxn];
int main(void)
{
    ll n, i, ans = 0, x;
    scanf("%lld", &n);
    for (i = 1;i <= n;i++)
    {
        scanf("%lld", &x);
        a[x] = i;
    }
    for (i = 1;i <= n;i++)
    {
        scanf("%lld", &x);
        ans += abs(i - a[x]);
    }
    printf("%lld\n", ans / 2);
    return 0;
}

 

posted @ 2021-05-04 00:27  lipu123  阅读(797)  评论(0)    收藏  举报