重新排列奶牛

今天我一如既往的打开了老师给我们做题的题库。看到了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;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值