题解 P3718 【[AHOI2017初中组]alter】

· · 题解

像这一题,要求最远距离最小,必定有二分的份啦~(暴力循环必死)

那么本题除了普通的二分,还有几个坑点(我也是被坑了好几次才A的):

这里先重点讲一下特判,由于字符串中的字符不是 N 就是 F ,所以想要让一排灯的不优美度为 1,有两种情况。

以字符串 FNNNFFNN 为例,如果最多能按 4 次开关,求最小的不优美度。

按照上面所说的,可以变成:

不难得出,想要让一排灯(灯数 > 1)不优美度为 1 共有两种情况,而变成这两种情况所需要按开关的次数之和正好是灯数。

所以我们只要特判出一种情况,灯数 - 需要按的次数 = 另一种情况需要按的次数。特判两个次数是不是小于等于可以按的次数。

完整的代码如下:

#include <iostream>
using namespace std;
int main()
{
    int n,k,p=0,g,t,ans;
    char c[2]={'F','N'}; //灯的状态对应的字符
    string s;
    cin >> n >> k >> s;
    for(int i=0;i < n;i++)
    if(s[i] == c[i % 2]) p++;
    if(p <= k || n-p <= k)
    {cout << 1; return 0;}
    //上面所说的特判方法
    int lb=2,rb=n / k+1,mb; //准备二分
    while(lb <= rb)
    {
        mb=(lb+rb) / 2; //得出可能的不优美度
        g=0;
        for(int i=0,j=0,l=0;i < n;i++)
        {
            if(s[j] == s[i]) l++;
            else j=i,l=1;
            if(mb < l) j=i+1,l=0,g++;
            //这个步骤请大家仔细思考。i 表示连续着的灯的最右段, j 表示连续着的灯的最左段,l 表示连续着的灯的长度,g 表示最少需要按多少次灯
        }
        if(g <= k)
        {
            rb=mb-1;
        } else
            lb=mb+1;
        //根据情况进行二分的分段
    }
    cout << lb;
    //输出最小的不优美度
    return 0;
}