[CF1525B]Permutation Sort

壹、题目描述 ¶

传送门 to CF.

中文翻译:给你一个长度为 \(n\) 的排列,每次可以任意调动一个连续子区间的元素,求把这个区间变成一个升序排列的最小次数。注意,你不能一次性选择整个排列。

贰、题解 ¶

显然,“各居其所” 显然是最棒的 —— 因为我们不需要任何操作,这个排列本身就是有序的。

那么次一点的呢?那就是 \(1\) 在自己的位置上,或者 \(n\) 在自己的位置上,这个时候我们只需要选择子区间 \([2,n]\) 或者 \([1,n-1]\) 就可以使排序有序。如果同时成立,即 \(p_1=1\and p_n=1\),那么选择 \([2,n-1]\) 重排就好了。

再次一点的呢?那就是 \(1\)\(n\) 都不在自己的位置上,即 \(p_1\neq 1\and p_n\neq n\),这个时候,我们只需要先进行一次操作,将 \(1\) 弄到一号位置(或者 \(n\) 弄到 \(n\) 号位置),就变成 “次一点” 的情况了。也就是说这种情况只需要两次咯?

不,不是的,如果只是那么简单,你会 \({\color{red}{\text{WA}}}\; \text{on test #2}\).

这说明这并没有那么简单,即我们前面的某个环节出问题了。哪里呢?注意到 “我们只需要先进行一次操作,将 \(1\) 弄到一号位置(或者 \(n\) 弄到 \(n\) 号位置)”,真的所有情况都只需要一步就可以到位?

考虑 \(p_1=n\and p_n=1\) 的情况,我们必须使用 \(2\) 次才能达到 “次一点” 的情形。

综上,我们有三种情况:

  • 第一种,“各居其所”,不需要;
  • 第二种,\(p_1=1\or p_n=1\),需要一次操作;
  • 第三种,\(p_1=n\and p_n=1\),需要三次操作;
  • 其他情况,两次操作;

判断一下就好了。

叁、参考代码 ¶

#define rep(i, l, r) for(int i=(l), i##_end_=(r); i<=i##_end_; ++i)
const int maxn=50;

int a[maxn+5], n;

signed main(){
    rep(tmp, 1, readin(1)){
        n=readin(1);
        rep(i, 1, n) a[i]=readin(1);
        int flg=1;
        rep(i, 1, n) if(a[i]!=i){
            flg=0; break;
        }
        if(flg){ printf("0\n"); continue; }
        if(a[1]==n && a[n]==1) printf("3\n");
        else if(a[1]!=1 && a[n]!=n) printf("2\n");
        else printf("1\n");
    }
    return 0;
}
posted @ 2021-05-19 20:48  Arextre  阅读(52)  评论(0编辑  收藏  举报