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;
}
posted @ 2022-02-13 19:23  zyy2001  阅读(73)  评论(0编辑  收藏  举报