1224. 交换瓶子

题目链接

1224. 交换瓶子

N 个瓶子,编号 1N,放在架子上。

比如有 5 个瓶子:

2 1 3 5 4

要求每次拿起 2 个瓶子,交换它们的位置。

经过若干次后,使得瓶子的序号为:

1 2 3 4 5

对于这么简单的情况,显然,至少需要交换 2 次就可以复位。

如果瓶子更多呢?你可以通过编程来解决。

输入格式

第一行包含一个整数 N,表示瓶子数量。

第二行包含 N 个整数,表示瓶子目前的排列状况。

输出格式

输出一个正整数,表示至少交换多少次,才能完成排序。

数据范围

1N10000,

输入样例1:

5 3 1 2 5 4

输出样例1:

3

输入样例2:

5 5 4 3 2 1

输出样例2:

2

解题思路

环,置换群

ai 连向位置为 ai 上的整数,即 aai,其构成 k 个环,最后一定是形成 n 个自环,而对于每个环内的整数,交换会增加一个环,交换不同环的两个整数会合并即减少一个环,所以最后至少进行 nk 次即能形成 n 个自环

贪心

可以想到一个贪心策略:从 i1n 枚举,如果不在对应位置上,则将 i 所在位置和当前数交换,即上述在环内交换两数

  • 时间复杂度:O(n)

代码

  • 贪心
// Problem: 交换瓶子 // Contest: AcWing // URL: https://www.acwing.com/problem/content/description/1226/ // Memory Limit: 64 MB // Time Limit: 1000 ms // // Powered by CP Editor (https://cpeditor.org) // %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } int res,n,a[10005],b[10005]; int main() { cin>>n; for(int i=1;i<=n;i++) { cin>>a[i]; b[a[i]]=i; } for(int i=1;i<=n;i++) if(a[i]!=i) { res++; swap(a[i],a[b[i]]); b[a[b[i]]]=b[i]; b[i]=i; } cout<<res; return 0; }
  • 置换群
// Problem: 交换瓶子 // Contest: AcWing // URL: https://www.acwing.com/problem/content/description/1226/ // Memory Limit: 64 MB // Time Limit: 1000 ms // // Powered by CP Editor (https://cpeditor.org) // %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } int res,n,cnt,a[10005]; bool v[10005]; int main() { cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1;i<=n;i++) if(!v[i]) { v[i]=true; cnt++; for(int j=a[i];!v[j];j=a[j])v[j]=true; } cout<<n-cnt; return 0; }

类似题目

1553. 用 Swap(0, i) 操作进行排序

给定一个 0N1 的随机排列,将其变为升序排列非常简单。

如果只能使用 Swap(0, *) 操作(将数字 0 和另一个数字交换位置),使得序列变成升序排列呢?

例如,给定序列 {4, 0, 2, 1, 3},我们可以通过下列交换操作,得到升序序列:

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

现在,请你求出将给定序列按如上交换操作变为升序序列,至少需要多少步操作。

输入格式

第一行包含整数 N,表示序列中元素个数。

第二行包含一个 0N1 的随机排列序列。

输出格式

输出所需最少操作步数。

数据范围

1≤N≤10^5$

输入样例:

10 3 5 7 2 6 4 9 0 8 1

输出样例:

9

置换群

和上题类似,不过只能用 0 交换,用 0 去合并其他非自环的环,每次合并需要一次交换操作,假设非 0 自环的个数为 cnt1,环的个数为 cnt,则需合并 cntcnt11 次,其他消成自环 n(1+cnt1) 次,共 n+cnt2cnt12

代码

// Problem: 用 Swap(0, i) 操作进行排序 // Contest: AcWing // URL: https://www.acwing.com/problem/content/1555/ // Memory Limit: 64 MB // Time Limit: 2000 ms // // Powered by CP Editor (https://cpeditor.org) // %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const int N=1e5+5; int a[N],n,cnt,cnt1; bool v[N]; int main() { cin>>n; for(int i=0;i<n;i++) { cin>>a[i]; if(a[i]!=0&&a[i]==i)cnt1++; } for(int i=0;i<n;i++) if(!v[i]) { cnt++; v[i]=true; for(int j=a[i];!v[j];j=a[j])v[j]=true; } cout<<n+cnt-2*cnt1-2; return 0; }

__EOF__

本文作者acwing_zyy
本文链接https://www.cnblogs.com/zyyun/p/15890100.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zyy2001  阅读(79)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示