重新排列奶牛
重新排列奶牛
农夫约翰的 头奶牛排成一排,编号 。
它们的排序可以由一个数组 来描述,其中 是位于位置 的奶牛的编号。
约翰希望将它们重新排列为一个不同的顺序。
新顺序用数组 来描述,其中 是位于位置 的奶牛的编号。
假设奶牛开始时的顺序为:
A = 5 1 4 2 3
并假设约翰希望它们重新排列为:
B = 2 5 3 1 4
为了从 顺序重新排列为 顺序,奶牛们进行了许多“循环”移位。
所谓循环移位,是指挑选排列中的若干头奶牛分在一组,组中奶牛进行循环移动位置,即第一头奶牛移动至第二头奶牛的位置,第二头奶牛移动至第三头奶牛的位置,…,最后一头奶牛移动至第一头奶牛的位置。
如上例中,将 号奶牛分在一组进行循环移位,移动过后, 号奶牛移动至位置 , 号奶牛移动至位置 , 号奶牛移动至位置 ;将 号奶牛分在另一组进行循环移位,移动过后, 号奶牛位于位置 , 号奶牛位于位置 ;最终完成重新排列。
每头奶牛都恰好参与一组循环移位,除非其在 中的位置没有变化。
请计算奶牛们完成重新排列,共需多少组循环移位,最长的一组循环移位的长度是多少。
输入格式
第一行包含整数 。
接下来 行包含 。
再接下来 行包含 。
输出格式
输出循环移位的组数,以及最长的一组循环移位的长度。
如果不存在循环移位,则第二个数输出 。
数据范围
,
输入样例:
5 5 1 4 2 3 2 5 3 1 4
输出样例:
2 3
解题思路
如果第个数在序列中的第个位置,并且如果有,那么我们就将与连一条有向边,方向从到,表示要与进行交换,这样才能够在序列中的这个位置。
以样例为例:
因此问题就变成了经过上面的操作后,一共有几个环(环中结点的数量要大于)。
然后题目表述的不是很清楚,当时一直想不明白如果遇到这种情况应该怎么交换。按照我一开始的理解,所谓的循环交换是指按照环的顺序进行移位,因此对于序列无论怎么移位,都只有这种情况,不可能会出现。然而事实是,这里的循环移位是指,选择其中一头牛,然后放到其他牛的地方。然后再把刚才被换出的牛放到其他没被换过的牛的地方,以此类推,直到最后一头牛,就放到空的位置上(这个空的位置就是一开始进行交换的牛的那个位置)。
以上面的例子为例,一开始先选择号牛,把放到第个位置,置换出号牛,然后再把号牛放到第个位置,置换出号牛,最后把号牛放到空的位置。
根据这些环的性质,我们可以发现这是一个环图的模型。因为如果一个点要放到另外一个点上,那么这个点就跟另外一个点连一条边,这样的话每一个点有且只有一条出边,和有且只有一条入边。
环图是指:
- 如果一个图是有向图,每个点有且只有一条出边,有且只有一条入边,那么这个图是环图。
- 如果一个图是无向图,每个点的的度数为,那么这个图是环图。
环图一定是由若干个环构成的。我们可以从任意一个结点出发,那么一定可以从这个点走到另外一个点,继续一直走下去,那么最后一定会走到起点。这是因为,再走的过程中,如果走不到起点就会一直走,由于我们点的数量是有限的,所以最终一定会走到之前走过的某一个点,如果这点不是起点,那么这个点的入度就为了,与每个点只有个入度产生矛盾,因此最终一定会走到起点。而且也不会走到其他的环上,一样会有入度为的矛盾。
这是一个有向图,但环与环之间是不相交的。要求环的数量,等价于求连通块的数量(一个图是有向图不等价于连通块,只有是环图才等价于连通块)。维护连通块就可以用并查集了。
AC代码如下:
1 #include <cstdio> 2 #include <algorithm> 3 using namespace std; 4 5 const int N = 110; 6 7 int a[N], pos[N]; 8 int fa[N], cnt[N]; 9 10 int find(int x) { 11 return fa[x] == x ? fa[x] : fa[x] = find(fa[x]); 12 } 13 14 int main() { 15 int n; 16 scanf("%d", &n); 17 for (int i = 1; i <= n; i++) { 18 scanf("%d", a + i); 19 } 20 for (int i = 1; i <= n; i++) { 21 int val; 22 scanf("%d", &val); 23 pos[val] = i; // pos[i]是把数字i映射到在数组b中的下标,这里存的是数字val在b中的位置 24 } 25 26 for (int i = 1; i <= n; i++) { 27 fa[i] = i; 28 cnt[i] = 1; 29 } 30 31 for (int i = 1; i <= n; i++) { 32 int x = a[i], y = a[pos[a[i]]]; 33 if (find(x) != find(y)) { 34 cnt[find(x)] += cnt[find(y)]; 35 fa[y] = find(x); 36 } 37 } 38 39 int ret = 0, maxv = 0; 40 for (int i = 1; i <= n; i++) { 41 if (fa[i] == i && cnt[i] > 1) { // 只统计长度大于1的环 42 ret++; 43 maxv = max(maxv, cnt[i]); 44 } 45 } 46 printf("%d %d", ret, ret == 0 ? -1 : maxv); 47 48 return 0; 49 }
参考资料
AcWing 1921. 重新排列奶牛(春季每日一题2022):https://www.acwing.com/video/3835/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/16183625.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效