图论(环)-蓝桥杯省赛C++B组-交换瓶子
题目:
有 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 [ i ] 与 交 换 成 i 。 对数组a,用数组下标i对应a[i],表示应当把a[i]与交换成i。 对数组a,用数组下标i对应a[i],表示应当把a[i]与交换成i。
从
图
论
角
度
考
虑
,
下
标
i
为
起
点
,
点
a
[
i
]
为
终
点
,
构
成
一
条
从
i
到
a
[
i
]
的
边
。
样
例
一
图
如
下
:
从图论角度考虑,下标i为起点,点a[i]为终点,构成一条从i到a[i]的边。样例一图如下:
从图论角度考虑,下标i为起点,点a[i]为终点,构成一条从i到a[i]的边。样例一图如下:
形
成
两
个
环
。
形成两个环。
形成两个环。
若
把
1
和
3
交
换
,
也
就
是
a
[
1
]
和
a
[
2
]
交
换
,
序
列
变
成
1
3
2
5
4
,
左
边
环
变
成
:
若把1和3交换,也就是a[1]和a[2]交换,序列变成1\ 3\ 2\ 5\ 4,左边环变成:
若把1和3交换,也就是a[1]和a[2]交换,序列变成1 3 2 5 4,左边环变成:
这
个
过
程
就
是
交
换
了
下
标
为
1
和
2
的
出
边
所
指
向
的
点
,
1
的
出
边
指
向
了
2
原
本
指
向
的
1
,
也
就
是
1
已
经
正
确
归
位
,
2
的
出
边
指
向
了
1
原
本
指
向
的
3
,
环
一
分
为
二
。
这个过程就是交换了下标为1和2的出边所指向的点,1的出边指向了2原本指向的1,也就是1已经正确归位,\\2的出边指向了1原本指向的3,环一分为二。
这个过程就是交换了下标为1和2的出边所指向的点,1的出边指向了2原本指向的1,也就是1已经正确归位,2的出边指向了1原本指向的3,环一分为二。
对
任
意
点
的
数
量
的
这
样
的
环
,
交
换
任
意
两
点
的
出
边
所
指
向
的
点
均
会
将
该
环
一
分
为
二
。
如
下
图
:
对任意点的数量的这样的环,交换任意两点的出边所指向的点均会将该环一分为二。如下图:
对任意点的数量的这样的环,交换任意两点的出边所指向的点均会将该环一分为二。如下图:
反 之 , 两 个 环 可 以 合 并 成 一 个 环 。 而 我 们 目 标 是 将 所 有 环 分 裂 成 本 身 , 就 是 要 a [ i ] = i , 就 必 须 不 断 分 裂 。 反之,两个环可以合并成一个环。而我们目标是将所有环分裂成本身,就是要a[i]=i,就必须不断分裂。 反之,两个环可以合并成一个环。而我们目标是将所有环分裂成本身,就是要a[i]=i,就必须不断分裂。
因 此 , 通 过 这 样 的 方 式 交 换 , 可 以 最 快 将 序 列 交 换 成 有 序 序 列 。 因此,通过这样的方式交换,可以最快将序列交换成有序序列。 因此,通过这样的方式交换,可以最快将序列交换成有序序列。
要 求 交 换 的 次 数 , 假 设 我 们 有 c n t 个 环 , 就 是 要 求 使 c n t 个 环 变 成 n 个 环 的 最 小 步 数 。 每 次 分 裂 增 加 一 个 环 , 最 终 的 步 数 就 是 n − c n t 。 因 此 , 统 计 环 的 数 量 即 可 。 要求交换的次数,假设我们有cnt个环,就是要求使cnt个环变成n个环的最小步数。\\每次分裂增加一个环,最终的步数就是n-cnt。因此,统计环的数量即可。 要求交换的次数,假设我们有cnt个环,就是要求使cnt个环变成n个环的最小步数。每次分裂增加一个环,最终的步数就是n−cnt。因此,统计环的数量即可。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define P pair<int,int>
#define x first
#define y second
#define ll long long
#define inf 0x7fffffff
using namespace std;
const int N=1e4+10;
int n,a[N];
bool vis[N];
int main()
{
cin>>n;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
int cnt=0;///统计环的个数
for(int i=1;i<=n;i++)
if(!vis[i])
{
int j=i;
while(!vis[j])
{
vis[j]=true;
j=a[j];
}
cnt++;
}
cout<<n-cnt<<endl;
return 0;
}