2021牛客寒假算法基础集训营2 F. 牛牛与交换顺序

链接:https://ac.nowcoder.com/acm/contest/9982/F
来源:牛客网

牛牛有一个数组,数组元素是1到n的排列,即数组的值在1~n范围内,且每个数字仅出现1次。
牛牛想要将该数组变为升序排列的,他可以进行如下的操作。

首先他要确定一个长度k,k的范围在1~n之间。
接下来他将会进行若干次操作。在每轮操作中他都可以选择一段长度为k的子数组,然后进行区间的翻转操作。

他可以做任意次数的操作,但是要求他每次选择的子数组区间满足li≤li+1li≤li+1,并且区间长度等于一开始选定的k,也就是说一旦某一次操作选择了数组的某个位置进行区间翻转操作,下一次做区间翻转的位置将会比上一次更靠右。

牛牛发现,并不总是存在一个k可以使得数组排序变为有序,请你告诉牛牛是否存在一个k能够在满足规则的情况下完成排序。

输入描述:

第一行输入一个正整数n(1≤n≤105)(1≤n≤105)表示数组的大小。
接下来输出一行n个正整数表示一个排列,即每个数的大小范围在1到n且每个正整数仅出现一次。

输出描述:

如果存在至少一个k能够使牛牛完成排序,请先在一行中输出一个"yes",然后另起一行输出一个可以满足排序的k,要求k的范围在[1,n][1,n]之间,如果有多解,你可以输出任意一个。反之如果不存在任何一个k可以完成排序,请直接在一行输出一个"no"

标程给的deque,但直接reverse也能过。没给k初始化导致WA到自闭QAQ

首先要知道k若存在则唯一。因为题目中限制了每次操作的子数组左端点下标一定是单调递增的,因此假设1~p-1已经有序,如果a[p] != p的话则一定要换。设a[q] = p,那么第一次操作的区间一定是p~q,因为只有这样才能把p换到他应该在的位置。如果不换的话,之后操作区间的左端点一定大于p,就再也没机会了。

因此遍历数组,找到第一个a[p] != p的位置,同时根据之前的pos数组找到p真正所在的下标,如此确定下来k的值,同时先把第一段区间reverse。之后要做的就是遍历数组,每当发现a[i] != i的情况就reverse,最后再遍历一遍判断是否有序。

这种写法得用scanf或者关同步的cin或者快读优化(赛后数据加强了

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <map>
#include <vector>
using namespace std;
int n, a[100005], pos[100005], k = 1, num;
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
        pos[a[i]] = i;
    }
    for (int i = 1; i <= n; i++)
    {
        if(a[i] != i)
        {
            k = pos[i] - i + 1;
            reverse(a + i, a + i + k);
            break;
        }
    }//然后判断这个k是否可行
    bool flag = 1;
    for (int i = 1; i <= n; i++)
    {        
        if(a[i] != i) 
        {
            reverse(a + i, a + i + k);
        }
    }
    for (int i = 1; i <= n; i++)
    {
        if (a[i] != i)
        {
            flag = 0;
            break;
        }
    }
    if(flag)
    { 
        cout << "yes" << endl;
        cout << k;
    }
    else cout << "no";
    return 0;
}
原文地址:https://www.cnblogs.com/lipoicyclic/p/14369828.html