首页 > 其他分享 >置换环

置换环

时间:2024-10-30 21:41:42浏览次数:2  
标签:return int 置换 交换 cin mp find

置换环

作用:求解数组排序元素间所需最小交换次数这类问题。

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

则总交换次数:\(ans = \sum_{i = 1}^n(cyclesize_i-1)=数组长度-环的个数\)

例题1:Problem - D - Codeforces

题意:给定一个长度为$ n(n≤2×10^5)$ 的排列 \(p\)。你可以多次交换排列中的任意两个数。问,最少多少次交换,可以使得排列中,有且仅有一个逆序对。

思路:利用上面的结论,我们要换的次数是\(n-环数+1\)。但是如果有相邻的两个数的话那么可以通过减少一次交换,使得其贡献出一个逆序对。次数的交换次数为\(n-环数-1\)

code:

int n, p[N], vis[N];
map<int, bool>ring;//记录环
bool flag = 0;
void dfs(int u) {
    if (vis[u]) return;
    vis[u] = 1;
    ring[u] = 1;
    int v = p[u];// u -> v
    if (ring[u - 1] || ring[u + 1])flag = 1;//记录是否有相邻的两个点
    dfs(v);
}
void slove() {
    cin >> n;
    for (int i = 1; i <= n; i++)vis[i] = 0;
    for (int i = 1; i <= n; i++)cin >> p[i];
    int cur = 0;
    flag = 0;
    for (int i = 1; i <= n; i++) {
        if (!vis[i]) {
            cur++;
            ring.clear();
            dfs(i);
        }
    }
    if (flag)cout << n - cur - 1 << endl;
    else cout << n - cur + 1 << endl;
}

例题2:Problem - E - Codeforces

题意:给你一个排列(\(1~n\)),这个排列被认为是简单的需要满足下面其中一个条件:

对于每一个\(i(1\le i\le n)\)

  • \(p_i = i\)
  • \(p_{p_i} = i\)

问你最少交换多少次使得这个排列是简单的。

思路:考虑置换环。

对于\(i\rightarrow p[i] \rightarrow p[p[i]] \rightarrow p[p[p[i]]] \rightarrow ... \rightarrow i\) 这样子的会构成一个环。需要满足条件的话,环的长度需要\(\ge 2\)。即,最后这个数列转化为图一定是若干个自环和若干个两数环所组成的图。

对于环长度\(\le 2\)的,要进行交换操作让环长度变短。我们发现让\(p[p[i]] = i\)比让\(p[i] = i\)更优,为什么呢?

我们设\(mp[i]\)为数值为\(i\)的下标。以\(2,3,4,1\)为例:

\(mp[1] = 4\),\(p[1] = 2\),我们想让\(p[p[1]] = 1\)即\(p[2] = 1\),那么\(p[mp[1]] = p[2]\),即\(p[4] = 3\)

交换完一次之后变成了\(2,1,4,3\)。同时更新\(mp[1] = 2,mp[3] = 4\)

这样必定可以一次修改两个数,是优于\(p[i]\)改成\(i\)的,那么对于环长度为\(size\)的,交换次数为\(\lfloor\dfrac{size-1}{2} \rfloor\)。

#include <bits/stdc++.h>
using namespace std;
struct DSU {
    std::vector<int> f, siz;
    
    DSU() {}
    DSU(int n) {
        init(n);
    }
    
    void init(int n) {
        f.resize(n + 1);
        std::iota(f.begin(), f.end(), 0);
        siz.assign(n + 1, 1);
    }
    
    int find(int x) {
        while (x != f[x]) {
            x = f[x] = f[f[x]];
        }
        return x;
    }
    
    bool same(int x, int y) {
        return find(x) == find(y);
    }
    
    bool merge(int x, int y) {
        x = find(x);
        y = find(y);
        if (x == y) {
            return false;
        }
        siz[x] += siz[y];
        f[y] = x;
        return true;
    }
    
    int size(int x) {
        return siz[find(x)];
    }
};


int main()
{
    int t; cin>>t;
    while(t--)
    {
        int n; cin>>n;
        DSU d(n);
        for(int i = 1;i <= n; i++)
        {
            int x; cin>>x;
            d.merge(i,x);
        }
        set<int>s;
        for(int i = 1;i <= n; i++){
            d.f[i] = d.find(i);
            s.insert(d.f[i]);
        }

        int ans = 0;
        for(auto x : s)
        {
            ans += (d.siz[x]-1)/2;
        }

        cout<<ans<<"\n";
    }
    return 0;
}

或者直接模拟上述过程也是可以的:

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 1e5 + 10;

int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    
    int t; cin>>t;
    while(t--)
    {
        int n; cin>>n;
        map<int,int>mp;
        vector<int>p(n+1);
        for(int i = 1;i <= n; i++){
            cin>>p[i];
            mp[p[i]] = i;
        }
        int ans = 0;
        for(int i = 1;i <= n; i++)
        {
            if(p[i] == i || p[p[i]] == i)
                continue;
            int s = mp[i],t = p[i];
            swap(p[s],p[t]);
            mp[p[s]] = s;
            mp[p[t]] = t;
            ans++;
        }
        cout<<ans<<"\n";
        
    }

    return 0;
}

//2 3 4 1
//2 1 4 3

//2 3 4 5 1
//2 1 4 5 3
//2 1 4 3 5

标签:return,int,置换,交换,cin,mp,find
From: https://www.cnblogs.com/nannandbk/p/18516681

相关文章

  • java毕业设计-基于springboot+vue的校园大学生二手交易跳蚤市场平台设计和实现,基于spr
    博主介绍:✌️码农一枚,专注于大学生项目实战开发、讲解和毕业......
  • HRC 004 T3 置换
    题目链接前置知识置换轮换\(60\space\text{pts}\)解法就像对于一个数,我们经常从素因子之积的角度看待它一样,在这道题中,我们从轮换的角度看待置换。我们考虑一个轮换变成恒等变换所需次数:一个长度为\(l\)轮换,可以看做一个边数为\(l\)的环,置换乘法可以看做数字沿着边转一......
  • 败者树、置换选择排序、最佳归并树
    败者树败者树用一个数组即可实现,而且,上图中的那些方块所代表的结点是不存储在败者树中的置换选择排序置换选择排序的目的是构造出比工作区更长的初始归并段,而更长就意味着初始归并段会更少,可能会减少归并的趟数,进而减少读写磁盘次数来优化排序时间。置换选择排序的核......
  • 操作系统-页面置换算法
    简介期末考试中常考的页面置换算法可能有三种,分别是先进先出(FIFO),最佳置换(OPT)和最久未使用(LRU)本篇文章会用一道例题来讲解这三种算法的思路和解题过程;题目假设有这样一个操作系统,其内存中有3个空闲页面框(题目也有可能是描述成M3,M是Memory的简写)。进程依次请求页面号为以下序......
  • 置换密码
    密码介绍:置换密码又叫换位密码只将明文字符改变顺序就得到密文一:列置换密码的加密设明文为“BeiJing2022OlympicWinterGames”密钥σ=(143)(56)将明文分为6列可得密钥(143)的意思是1列的位置换到4列,4列的位置换到3列,3列的位置换到1列(56)同理,2位置不变再竖着抄下来......
  • 9章11节:用R实现区组随机化和置换区组随机化
    区组随机化是一种常用的随机化方法,尤其适用于临床试验设计中。它的主要优势是能够在治疗组间保持样本量的一致性,并在不同组之间均衡混杂因素。然而,这种方法也有其固有的缺点,如研究者在未设盲的情况下,可能对研究对象的分配产生预测,导致选择偏倚。为了解决这一问题,置换区组随机......
  • 小林coding学习笔记(内存页面置换算法)
    缺页中断示意图1在CPU里执行一条查找某个页面A的指令,首先是虚拟内存,会到虚拟内存和物理内存的映射关系的页表中查询。2页表中不存在需要查找的页面A的有效信息。3则触发缺页中断信号给操作系统,操作系统收到缺页中断信号后,就会去磁盘里面查找该页面。4操作系统在磁盘中查......
  • PyTorch--Tensor拼接、切分、置换
    目录1、拼接torch.cat()torch.stacks()2、切分torch.chunk()torch.split() 3、置换1、拼接torch.cat()torch.cat(tensors,dim=0,out=None):将张量按照dim维度进行拼接torch.stacks()torch.stacks(tensors,dim=0,out=None):将张量在新创建的dim维度上进行拼接(te......
  • 【2025】基于springboot二手闲置物品置换平台(源码+文档+调试+答疑)
    ......