置换环

1 作用

置换环是用来求解数组排序元素间所需最小交换次数这类问题。

置换环思想:置换环将每个元素指向其排序后应在的位置,最终首位相连形成一个环(若数字在最终位置,则其自身成环),可知元素之间的交换只会在同一个环内进行,而每个环内的最小交换次数为

环上的元素 - 1

排序所需最小交换次数为数组长度-环的个数

考虑置换环,也就是 i↔a[i] 之间连边。

例如以下排列 [1,7,5,8,2,6,3,4]

1 - >1      6  - > 6     2  - > 7 - > 3 - > 5 - > 2

我们看看那个4个点的环

 

在排列上,进行交换

2 3 5 7       ------------------->      2 3 5 7 

7 5 2 3        ------------------->     3 5 2 7   (交换a[2],a[7])

 

2 3 5 7       ------------------->      2 3 5 7 

3 5 2 7       ------------------->     2 5 3 7   (交换a[2],a[5])

 

2 3 5 7       ------------------->      2 3 5 7 

2 5 3 7        ------------------->     2 3 5 7   (交换a[3],a[5])

 

执行了3次交换,就可以使得4个数,到 [1,2,3,4,5...n] 相应的位置上。

如果有 m 个环,那么只需要 n−m 次交换就可以把排列变成 [1,2,3...n] 了。

然后执行一次相邻交换的操作就可以了。

但是,还有一种情况。

对于上面的4个点的环,我们这样交换

2 3 5 7       ------------------->      2 3 5 7 

7 5 2 3        ------------------->     3 5 2 7   (交换a[2],a[7])

 

2 3 5 7       ------------------->      2 3 5 7 

3 5 2 7       ------------------->      3 2 5 7   (交换a[3],a[5])

此时,我们交换了两次,而 a[2],a[3] 构成了一个逆序对。

也就是,如果环中,有相邻的两个点,那么可以通过减少一次交换,使得其贡献出一个逆序对。

此时的操作次数为 n−m−1

 例题
 
代码:
// Problem: D. Lucky Permutation
// Contest: Codeforces - Codeforces Round 842 (Div. 2)
// URL: https://codeforces.com/contest/1768/problem/D
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

using namespace std;
#define int long long 
#define sc(x) scanf("%lld",&x)
#define pr(x) printf("%lld\n",x)
#define YES puts("YES");
#define NO puts("NO");
#define Yes puts("Yes");
#define No puts("No");
#define fo(i,j,k) for(int i = j;i <= k;i ++)
#define pre(i,j,k) for(int i = j;i >= k;i --)
#define ls idx*2
#define rs idx*2+1
int pow_mod(int a1, int n1, int m1)
{
    if(n1 == 0) return 1;
    int x = pow_mod(a1, n1/2, m1);
    long long ans = (long long)x * x % m1;
    if(n1 % 2 == 1) ans = ans *a1 % m1;
    return (int)ans;
}
inline __int128 read(){
    __int128 x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if(ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}
inline void print(__int128 x){
    if(x < 0){
        putchar('-');
        x = -x;
    }
    if(x > 9)
        print(x / 10);
    putchar(x % 10 + '0');
}
// bool operator<(NOW a, NOW b) {
    // if(a.site == b.site)
    // return a.x > b.x;
    // return a.site< b.site;
// }
const int N = 1e6 + 10;
int n,m;
int a[N],b[N];
bool f;
map<int,int>mp;
void dfs(int u)
{
    if(b[u])
    return ;
    b[u] = 1;
    mp[u] = 1;
    if(mp[u+1] == 1||mp[u-1]== 1)
    f = true;
    dfs(a[u]);
}
void solve()
{
    cin >> n;
    for(int i = 1;i <= n;i ++)
    cin >> a[i],b[i] = 0;
    int num = 0;
    f = false;
    for(int i = 1;i <= n;i ++)
    {
        if(!b[i])
        {
            num ++;
            mp.clear();
            dfs(i);
        }
    }
    //cout << num << '\n';
    if(f)
    cout << n - num - 1 << '\n';
    else
    cout << n-num+1 << '\n';
}
signed main()
{
    int tt = 1;
    sc(tt);
    while(tt--)
    {
        solve();
    }
}
View Code

 

 

posted @ 2023-06-14 16:22  清初  阅读(245)  评论(0)    收藏  举报