首页 > 试题广场 >

小苯的魔法染色

[编程题]小苯的魔法染色
  • 热度指数:1087 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
\hspace{15pt}小红面前有一堵长度为 n 的墙,用一个只由 \tt W(白色)和 \tt R(红色)组成的字符串 a_1a_2\dots a_n 表示。她希望最终将整面墙全部染成红色。

\hspace{15pt}为此她请来了魔法师小苯。一次施法的流程如下:
\hspace{23pt}\bullet\,小苯选择一个闭区间 [l,r]\ (1\leqq l\leqq r\leqq n)
\hspace{23pt}\bullet\,立刻将区间内的所有格子染成红色。

\hspace{15pt}小苯至多施法 m 次,且每次施法的区间长度 \left(r-l+1\right) 不得超过 k

\hspace{15pt}现在小苯想知道,将整堵墙染成红色所需的最小 k 是多少。请你求出这个 k 的最小可能值。

输入描述:
\hspace{15pt}输入包含两行。

\hspace{15pt}第一行输入两个正整数 n,m\ \left(1\leqq m\leqq n\leqq 2\times10^5\right)——墙的长度与小苯允许施法的最大次数。

\hspace{15pt}第二行输入一个长度为 n 的字符串 s,保证 s 仅由字符 \tt W\tt R 组成。


输出描述:
\hspace{15pt}输出一个正整数,表示满足要求的最小 k
示例1

输入

5 2
WRWWR

输出

2

说明

小苯可以进行 m = 2 次操作,每次操作的长度必须在 2 以内。
一种可能的染色方式是:选择 [1, 2] 再选择 [3,4],操作后整面墙都会被染红,可以证明不存在单次操作比 2 更小的长度。
推荐
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
//#define inf 1e18
typedef long long ll;
#define int long long


int n, m;
string s;

bool check(int mid) {
    vector<int> dp(n + 1, 1e18);
    dp[0] = 0;
    for(int i = 1; i <= n; i++) {
        if(s[i] == 'R') {
            dp[i] = dp[i - 1];
        }
        else {
            dp[i] = dp[max(0LL, i - mid)] + 1;
        }
    }
    return dp[n] <= m;
}

void solve() {
    cin >> n >> m >> s;
    s = " " + s;
    int l = 0, r = n;
    while(l < r) {
        int mid = (l + r) >> 1;
        if(check(mid)) r = mid;
        else l = mid + 1;
    }
    cout << l << endl;
}


/*	

5 2
WRWWR
2

3 1
RRR

3 3
WWW

*/

signed main () {
//     init(minp, primes, m); // primes
    // init();
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int _ = 1;
//   cin >> _;
    while(_ -- ) {
        solve();
    }
    return 0;
}

编辑于 2025-02-18 17:23:21 回复(0)
import java.util.*;
import java.io.*;

public class Main {
    static int n, m;
    static String s;
    public static void main(String[] args) {
        try(BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
            String[] line = br.readLine().split("\\s+");
            n = Integer.parseInt(line[0]);
            m = Integer.parseInt(line[1]);
            s = br.readLine();
            boolean hasW = false;
            for (int i = 0; i < s.length(); ++i) {
                if (s.charAt(i) == 'W') {
                    hasW = true;
                    break;
                }
            }
            if (!hasW) {
                System.out.println(0);
                return;
            }
            int left = 1, right = n / m + (n % m) + 1;
            while (left < right) {
                int mid = left + ((right - left) >> 1);
                int i = 0, j = m;
                while (i < s.length()) {
                    if (s.charAt(i) == 'R') {
                        ++i;
                    } else {
                        i += mid;
                        --j;
                        if (j < 0) {
                            break;
                        }
                    }
                }
                if (j < 0) {
                    // k要开大一些
                    left = mid + 1;
                } else {
                    // k可以继续缩小
                    right = mid;
                }
            }
            System.out.println(left);
        } catch (IOException ie) {}
    }
}
发表于 2025-04-09 20:27:55 回复(0)
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt(), m = in.nextInt(), left = -1, right = n, last = n;
        String a = in.next();
        // 记录下一个白色块的位置,方便快速搜索
        int[] next = new int[n];
        for (int i=n-1; i>=0; i--) {
            if (a.charAt(i) == 'W') {
                last = i;
            }
            next[i] = last;
        }
        // 二分查找,区间左开右开(left, right)
        while (left + 1 < right) {
            int k = left + ((right - left) >> 1);
            if (check(next, k, n, m)) {
                right = k;
            } else {
                left = k;
            }
        }
        System.out.println(right);
    }

    private static boolean check(int[] next, int k, int n, int m) {
        int cnt = 0, index = 0;
        // next[index]表示从index开始的第一个W的位置
        // 每次染色表示位置+k,将所有包含W的染完的次数不超过m即可行
        while (index < n && next[index] < n) {
            // 找到下一个W的位置
            index = next[index];
            // 从当前的W块开始,染色k个块,继续检查后续
            index += k;
            // 染色次数超过m表示方案不可行
            cnt++;
            if (cnt > m) {
                return false;
            }
        }
        return true;
    }
}

编辑于 2025-03-11 17:39:16 回复(0)