今天我一如既往的打开了老师给我们做题的题库。看到了USACO的题。
不得不说(我绝对不会说是我想做)
好了,说正事。
先来看题目。
描述
农夫约翰的 N 头奶牛排成一排,编号 1∼N。
它们的排序可以由一个数组 A 来描述,其中 A(i) 是位于位置 i 的奶牛的编号。
约翰希望将它们重新排列为一个不同的顺序。
新顺序用数组 B 来描述,其中 B(i) 是位于位置 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(i)。
再接下来 N 行包含 B(i)。
输出描述
输出循环移位的组数,以及最长的一组循环移位的长度。
如果不存在循环移位,则第二个数输出 −1。
用例输入:
5
5
1
4
2
3
2
5
3
1
4
用例输出:
2 3
说一下思路:
我们先来看样例输入。
我们可以清晰地看到每头牛的动向,那如果我们把他们画成图:
我们发现,每次循环移位,都会形成一个环,并且每头奶牛只参与这一次循环移位。
这下就好办了,我们定义数组a存储原来的奶牛顺序,数组b存储现在的奶牛顺序,数组f存储在环中他的下一个点,n存储奶牛的数量,ans和ans2分别存储答案,数组c存储这个点是否被遍历过了。
代码如下:
int a[105], b[105], f[105], n, ans, ans2;
bool c[105];
然后是平平无奇的输入:
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
for (int i = 1; i <= n; i++)
scanf("%d", &b[i]);
接下来,我们把每一个点的下一个点存储在f[i]里:
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if (a[i] == b[j])
{
f[i] = j;
break;
}
遍历每一个点,如果这个点出于新的环里,我们就进行遍历:
for (int i = 1; i <= n; i++)
if (c[i] == false && f[i] != i)
{
ans++;
ans2 = max(ans2, find(i, 0));
}
然后输出,注意这里我们要进行特判:
if (ans2 == 0)
printf("%d -1\n", ans);
else
printf("%d %d\n", ans, ans2);
最后是负责遍历的函数:
int find(int now, int length)
{
if (c[now] == true)
return length;
c[now] = true;
find(f[now], length + 1);
}
我们把上面的代码段组合一下,得到代码:
#include <bits/stdc++.h> // 万能头文件
using namespace std;
int a[105], b[105], f[105], n, ans, ans2; // f存储了每个点的下一个点
bool c[105]; // c存储了每一个我们遍历过的点
int find(int now, int length)
{
if (c[now] == true) // 说明我们已经遍历了这个环上的每一个点
return length;
c[now] = true; // 标记
find(f[now], length + 1); // 遍历这个环的下一个点
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
for (int i = 1; i <= n; i++)
scanf("%d", &b[i]);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if (a[i] == b[j])
{
f[i] = j; // 说明i的下一个点是j
break;
}
for (int i = 1; i <= n; i++)
if (c[i] == false && f[i] != i) // 如果i点没有被标记过,则把i点所在环上所有的点标记一遍
{
ans++;
ans2 = max(ans2, find(i, 0)); // 标记
}
if (ans2 == 0)
printf("%d -1\n", ans);
else
printf("%d %d\n", ans, ans2); // 输出答案
return 0;
}