重新排列奶牛
重新排列奶牛
农夫约翰的 $N$ 头奶牛排成一排,编号 $1 \sim N$。
它们的排序可以由一个数组 $A$ 来描述,其中 $A \left( i \right)$ 是位于位置 $i$ 的奶牛的编号。
约翰希望将它们重新排列为一个不同的顺序。
新顺序用数组 $B$ 来描述,其中 $B \left( i \right)$ 是位于位置 $i$ 的奶牛的编号。
假设奶牛开始时的顺序为:
A = 5 1 4 2 3
并假设约翰希望它们重新排列为:
B = 2 5 3 1 4
为了从 $A$ 顺序重新排列为 $B$ 顺序,奶牛们进行了许多“循环”移位。
所谓循环移位,是指挑选排列中的若干头奶牛分在一组,组中奶牛进行循环移动位置,即第一头奶牛移动至第二头奶牛的位置,第二头奶牛移动至第三头奶牛的位置,…,最后一头奶牛移动至第一头奶牛的位置。
如上例中,将 $5,1,2$ 号奶牛分在一组进行循环移位,移动过后,$5$ 号奶牛移动至位置 $2$,$1$ 号奶牛移动至位置 $4$,$2$ 号奶牛移动至位置 $1$;将 $4,3$ 号奶牛分在另一组进行循环移位,移动过后,$4$ 号奶牛位于位置 $5$,$3$ 号奶牛位于位置 $3$;最终完成重新排列。
每头奶牛都恰好参与一组循环移位,除非其在 $A,B$ 中的位置没有变化。
请计算奶牛们完成重新排列,共需多少组循环移位,最长的一组循环移位的长度是多少。
输入格式
第一行包含整数 $N$。
接下来 $N$ 行包含 $A \left( i \right)$。
再接下来 $N$ 行包含 $B \left( i \right)$。
输出格式
输出循环移位的组数,以及最长的一组循环移位的长度。
如果不存在循环移位,则第二个数输出 $?1$。
数据范围
$1 \leq N \leq 100$,
$1 \leq A \left( i \right),B \left( i \right) \leq N$
输入样例:
5 5 1 4 2 3 2 5 3 1 4
输出样例:
2 3
解题思路
如果第$i$个数$a \left[ i \right]$在序列$b$中的第$j$个位置,并且如果有$i \ne j$,那么我们就将$a \left[ i \right]$与$a \left[ j \right]$连一条有向边,方向从$a \left[ i \right]$到$a \left[ j \right]$,表示$a \left[ i \right]$要与$a \left[ j \right]$进行交换,这样$a \left[ i \right]$才能够在序列$b$中$j$的这个位置。
以样例为例:
因此问题就变成了经过上面的操作后,一共有几个环(环中结点的数量要大于$1$)。
然后题目表述的不是很清楚,当时一直想不明白如果遇到$a = \left[ {1, 3, 2} \right],~ b = \left[ {3, 2, 1} \right]$这种情况应该怎么交换。按照我一开始的理解,所谓的循环交换是指按照环的顺序进行移位,因此对于序列$1~3~2$无论怎么移位,都只有$2~1~3,~3~2~1,~1~3~2$这$3$种情况,不可能会出现$3~2~1$。然而事实是,这里的循环移位是指,选择其中一头牛,然后放到其他牛的地方。然后再把刚才被换出的牛放到其他没被换过的牛的地方,以此类推,直到最后一头牛,就放到空的位置上(这个空的位置就是一开始进行交换的牛的那个位置)。
以上面的例子为例,一开始先选择$3$号牛,把$3$放到第$1$个位置,置换出$1$号牛,然后再把$1$号牛放到第$3$个位置,置换出$2$号牛,最后把$2$号牛放到空的位置。
根据这些环的性质,我们可以发现这是一个环图的模型。因为如果一个点要放到另外一个点上,那么这个点就跟另外一个点连一条边,这样的话每一个点有且只有一条出边,和有且只有一条入边。
环图是指:
- 如果一个图是有向图,每个点有且只有一条出边,有且只有一条入边,那么这个图是环图。
- 如果一个图是无向图,每个点的的度数为$2$,那么这个图是环图。
环图一定是由若干个环构成的。我们可以从任意一个结点出发,那么一定可以从这个点走到另外一个点,继续一直走下去,那么最后一定会走到起点。这是因为,再走的过程中,如果走不到起点就会一直走,由于我们点的数量是有限的,所以最终一定会走到之前走过的某一个点,如果这点不是起点,那么这个点的入度就为$2$了,与每个点只有$1$个入度产生矛盾,因此最终一定会走到起点。而且也不会走到其他的环上,一样会有入度为$2$的矛盾。
这是一个有向图,但环与环之间是不相交的。要求环的数量,等价于求连通块的数量(一个图是有向图不等价于连通块,只有是环图才等价于连通块)。维护连通块就可以用并查集了。
AC代码如下:
1 #include2 #include 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/