CXB 移动“哨兵棋子”

题解

        一条数轴有N个棋子,每个棋子可以占据一个整数位置。N个棋子目前位于不同的整数位置,现在你想要移动它们以便它们占据N个连续位置(例如,位置5,6,7)。当前所有棋子中位置最小或者位置最大的棋子,称为“哨兵棋子”。每一次,你只能移动“哨兵棋子”。你可以把“哨兵棋子移动到当前任何未占用的整数位置,前提是在这个新位置该棋子不再是“哨兵棋子。”请确定使得N个棋子最终占据连续的N个位置的最小移动次数和最大移动次数。

输入格式

        第一行,一个整数N。3<=N<=100000。

        接下来有N行,第i行是一个整数,表示第i个棋子的位置,整数范围[1,1000000000]。

输出格式

        第一行,一个整数,最小移动次数。

        第二行,一个整数,最大移动次数。

输入样例

3

7

4

9

输出样例

1

2

题解

        先说最小次数。

        我们把最后的连续位置看作一个区间,那么最小移动次数实际上就是$n$减去这个区间原有的棋子数量,用尺取法求即可。

         但是这里要特判一种情况,就是整条数轴只分成两部分连续的棋子,且其中一部分只有一个棋子。容易得到,如果这两个部分之间间隔为$1$,那么最小移动次数就是$1$。否则,先把另一部分中的一个棋子移到那个部分的一边,再把只有一个棋子部分的棋子移进刚刚移出的空,最小移动次数为$2$。

        最大次数也很简单,实际上最大移动次数就是棋子一个一个“滚”到一起,如下所示:

___#_#___#___

_____##__#___

______##_#___

_______###___

        容易想到,最大移动次数就是第$1$个棋子到第$n$个棋子之间的空位数量减去刚开始移动一个哨兵棋子所减少的最小空格数量。

#include <iostream>
#include <cstdio>
#include <algorithm>

#define MAX_N (100000 + 5)

using namespace std;

int n;
int a[MAX_N];
int ans1, ans2;

int main()
{
     scanf("%d", &n);
     for(register int i = 1; i <= n; ++i)
     {
         scanf("%d", a + i);    
    }
    sort(a + 1, a + n + 1);
    ans1 = n;
    for(register int i = 1, j = 1; j <= n; ++j)
    {
        if(a[j] - a[i] < n) ans1 = min(ans1, n - (j - i + 1));
        else ++i;
    }
    if(a[n] - a[2] + 1 == n - 1 && a[2] - a[1] - 1 > 1)
    {
        ans1 = 2;
    }
    else if(a[n - 1] - a[1] + 1 == n - 1 && a[n] - a[n - 1] - 1 > 1)
    {
        ans1 = 2;
    }
    ans2 = a[n] - a[1] - (n - 1) - min(a[2] - a[1] - 1, a[n] - a[n - 1] - 1);
    printf("%d
%d", ans1, ans2);
    return 0;
}
参考程序
原文地址:https://www.cnblogs.com/kcn999/p/10743076.html