AcWing 1224. 交换瓶子【第七届蓝桥杯省赛C++B组,第七届蓝桥杯省赛JAVAA组】


一、题目链接

AcWing 1224. 交换瓶子
在这里插入图片描述
在这里插入图片描述


二、题目分析

(一)算法标签

图论 环 置换群 贪心

(二)解题思路

解法一思路:

这里以第一个样例为例,要满足题目要求,即最后的排列应该是1, 2, 3, 4, 5
则3要跟2换位置,2要跟1换位置,1要跟3换位置 (即1 → \rightarrow 3 → \rightarrow 2 → \rightarrow 1),从而形成一个环;
5要跟4换位置,4要跟5换位置,(即4 → \rightarrow 5 → \rightarrow 4),又形成一个环。
我们知道,
对于一个环,交换环中两个元素的位置,则必然会裂开成2个环(环的数量加1)
这里以环(1 → \rightarrow 3 → \rightarrow 2 → \rightarrow 1)为例,如果交换1和3,则环裂开成环(1 → \rightarrow 1) 和 环(3 → \rightarrow 2 → \rightarrow 3) [即原来指向1的现在指向3,原来指向3的现在指向1]
对于两个不同的环,交换这两个不同环中的元素,则这两个环会合并成一个环
这里以环(1 → \rightarrow 3 → \rightarrow 2 → \rightarrow 1) 和 环(4 → \rightarrow 5 → \rightarrow 4)为例,交换1和4,则这两个环会合并成环(1 → \rightarrow 3 → \rightarrow 2 → \rightarrow 4 → \rightarrow 5 → \rightarrow 1)[即原来指向1的现在指向4,原来指向4的现在指向1]

要满足题目要求(即要裂开成n个自环,即1要到1的位置,2要到2的位置,以此类推),也就是说从cnt个环要变成n个自环,则至少要交换n - cnt次(其中cnt为环的个数)

那么,如何求环的个数呢?

对于每一个数,如果没有在环中,则将这个数和这个数的应该要去的数的位置也标记成true,直到某个为true,则说明它属于另一个环,再对下一个数进行重复操作,代码如下:

for (int i = 1; i <= n; i ++ )
{
    if (!st[i])
    {
        cnt ++ ;    // 环的数量+1
        for (int j = i; !st[j]; j = b[j])   // 下标为j的元素应该要到元素b[j]的位置
        {
            st[j] = true;
        }
    }
}

解法二思路:

遍历每一个数,如果当前数不在应在的位置上,则交换当前数和它应该在的位置上的数,直到当前数在应该在的位置上为止。
每交换一次,则答案数+1


三、AC代码

解法一:

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1e4 + 10;

int b[N];
bool st[N];

int n;

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i ++ ) scanf("%d", &b[i]);
    
    int cnt = 0;
    for (int i = 1; i <= n; i ++ )
    {
        if (!st[i])
        {
            cnt ++ ;    // 环的数量+1
            for (int j = i; !st[j]; j = b[j])   // 下标为j的元素应该要到元素b[j]的位置
            {
                st[j] = true;
            }
        }
    }
    
    cout << n - cnt << endl;
    return 0;
}

解法二:

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1e4 + 10;

int b[N];

int n;

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i ++ ) scanf("%d", &b[i]);
    
    int cnt = 0;
    for (int i = 1; i <= n; i ++ )
    {
        while (b[i] != i)
        {
            swap(b[i], b[b[i]]);
            cnt ++ ;
        }
    }
    
    cout << cnt << endl;
    return 0;
}

四、其它题解

AcWing 1224. 交换瓶子

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值