1224. 交换瓶子
题目链接
1224. 交换瓶子
有 \(N\) 个瓶子,编号 \(1∼N\),放在架子上。
比如有 \(5\) 个瓶子:
2 1 3 5 4
要求每次拿起 \(2\) 个瓶子,交换它们的位置。
经过若干次后,使得瓶子的序号为:
1 2 3 4 5
对于这么简单的情况,显然,至少需要交换 \(2\) 次就可以复位。
如果瓶子更多呢?你可以通过编程来解决。
输入格式
第一行包含一个整数 \(N\),表示瓶子数量。
第二行包含 \(N\) 个整数,表示瓶子目前的排列状况。
输出格式
输出一个正整数,表示至少交换多少次,才能完成排序。
数据范围
\(1≤N≤10000,\)
输入样例1:
5
3 1 2 5 4
输出样例1:
3
输入样例2:
5
5 4 3 2 1
输出样例2:
2
解题思路
环,置换群
将 \(a_i\) 连向位置为 \(a_i\) 上的整数,即 \(a_{a_i}\),其构成 \(k\) 个环,最后一定是形成 \(n\) 个自环,而对于每个环内的整数,交换会增加一个环,交换不同环的两个整数会合并即减少一个环,所以最后至少进行 \(n-k\) 次即能形成 \(n\) 个自环
贪心
可以想到一个贪心策略:从 \(i\in 1\sim n\) 枚举,如果不在对应位置上,则将 \(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) 操作进行排序
给定一个 \(0∼N−1\) 的随机排列,将其变为升序排列非常简单。
如果只能使用 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\),表示序列中元素个数。
第二行包含一个 \(0∼N−1\) 的随机排列序列。
输出格式
输出所需最少操作步数。
数据范围
1≤N≤10^5$
输入样例:
10
3 5 7 2 6 4 9 0 8 1
输出样例:
9
置换群
和上题类似,不过只能用 \(0\) 交换,用 \(0\) 去合并其他非自环的环,每次合并需要一次交换操作,假设非 \(0\) 自环的个数为 \(cnt_1\),环的个数为 \(cnt\),则需合并 \(cnt-cnt_1-1\) 次,其他消成自环 \(n-(1+cnt_1)\) 次,共 \(n+cnt-2*cnt_1-2\) 次
代码
// 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;
}