Feeding the Cows (USACO Bronze 2022 December)

Feeding the Cows (USACO Bronze 2022 December)

N=105N=10^5头G牛或H牛站成一排,每头牛需要KK距离内有一块对应的G草或H草,输出最节省的种草方案。

潜在总方案数很大(21052^{10^5}),所以我们看看是否有贪心这样的快速的策略。如果我们从左往右扫描的话,对于一头没有草地覆盖的牛,位置在ii, 我们可以选择往右最远的地方,也就是i+Ki+K种草,这样可以覆盖最多的后续的牛。思考一下可以发现,这个策略应该是最优的。一般情况下, 这个位置肯定是空的,因为我们是从左往右扫描的。

唯一剩下的问题,是到最后的时候,最远的地方可能已经超出了边界,这时需要处理一下。简单地想,我们可以从右往左回过来扫描找一个空的位置, 这时要问,是否一定存在一个能覆盖当前牛,但又是空的位置?答案是肯定的,试一些KK的值和不同的情况,可以直观地看出来的确是这样的,比赛现场就可以直接写的。如果需要证明,可以这样:

首先这时K肯定大于0,因为K=0的时候,每头牛脚下都种一块草,没有超出边界这个问题。 所以K>0K>0,此时当i+K>Ni+K>N时,我们可以证明最后两块地方(N1N-1NN)一定至少有一块是空的,因为如果这两块都是满的,一定一个是G草,一个是H草(同样草相邻显然是浪费的), 必然已经覆盖了当前的牛,与当前牛没有草地覆盖相矛盾。所以NNN1N-1中至少有一个空地,种上草就可以。

所以我们找到了一个最优的贪心策略。

复杂度是O(N)\mathcal{O}(N)

#include <cstdio>
#include <algorithm>
#include <functional>
#include <vector>
#include <cstring>
using namespace std;
 
int T,n,k;
char s[100005];
char g[100005];
 
// place ideally at x. if x is out of bounds, then find a place
// return actual place
int place(int x, char c) {
    if (x < n) {
        g[x] = c;
        return x;
    }
    for (int i = n-1; i >= 0; i--)
        if (g[i] == '.') {
            g[i] = c;
            return i;
        }
    return -1;
}
 
int main() {
    scanf("%d", &T);
    for (int t = 0; t < T; t++) {
        scanf("%d %d", &n, &k);
        scanf("%s", s);
        memset(g, '.', n+1);
 
        int gend = 0, hend = 0;
        int cnt = 0;
        for (int i = 0; i < n; i++) {
            if (s[i] == 'G' && i >= gend) {                
                gend = place(i+k, 'G') + k + 1;
                cnt++;
            }
            if (s[i] == 'H' && i >= hend) {
                hend = place(i+k, 'H') + k + 1;
                cnt++;
            }
        }
        printf("%d\n", cnt);
        for (int i = 0; i < n; i++)
            printf("%c", g[i]);
        printf("\n");
    }
 
    return 0;
}