本题是USACO2022年到2023年赛季12月第一次比赛中,青铜组第二题。客观来说,从第二题开始就需要有一定的技巧性和思维上的提升了。这道题目主要可以从贪心和模拟的思路去思考,要能够想到如果设计草坪的铺放,让它能够覆盖更多的奶牛。
Farmer John 有 N(1≤N≤10^5)头奶牛,每头奶牛的品种是更赛牛(Guernsey)或荷斯坦牛(Holstein)之一。她们沿水平方向排成一行,奶牛们占据的位置编号为1…N。
由于奶牛们都饿了,FJ 决定在1…N 中的某些位置上种植草地。更赛牛和荷斯坦牛喜欢不同类型的草,所以如果 Farmer John 决定在某个位置种草,他必须选择种植更赛牛喜欢的草或荷斯坦牛喜欢的草——他不能在同一个位置同时种两种草。种植的每一片草地都可以喂饱数量不限的相应品种的奶牛。
每头奶牛愿意移动至多 KK(0≤K≤N−1)个位置以前往一个草地。求出喂饱所有奶牛所需种植的最小草地数量。此外,输出一种使用最小草地数量喂饱所有奶牛的种植方案。任何满足上述条件的方案均视为正确。
输入格式(从终端 / 标准输入读入):
每个测试用例包含 T 个子测试用例,为一种奶牛的排列。输入的第一行包含 T(1≤T≤10)。以下是 T 个子测试用例。
每个子测试用例的第一行包含 N 和 K。第二行包含一个长度为 N 的字符串,其中第 i 个字符表示第 i 头奶牛的品种(G 表示更赛牛,H 表示荷斯坦牛)。
输出格式(输出至终端 / 标准输出):
对于 T 个子测试用例中的每一个,输出两行。第一行输出喂饱所有奶牛所需的最小草地数量。第二行输出一个长度为 N 的字符串,表示一种使用最小草地数量喂饱所有奶牛的种植方案。第 i 个字符表示第 i 个位置,若不种草则为 '.',若种植喂饱更赛牛的草则为 'G',若种植喂饱荷斯坦牛的草则为 'H'。任何合法的方案均可通过。
输入样例:
6 5 0 GHHGG 5 1 GHHGG 5 2 GHHGG 5 3 GHHGG 5 4 GHHGG 2 1 GH输出样例:
5 GHHGG 3 .GH.G 2 ..GH. 2 ...GH 2 ...HG 2 HG注意对于某些子测试用例,存在多种可通过的方案使用最小数量的草地。例如,在第四个子测试用例中,以下是另一个可以通过的答案:
.GH..这个方案在第二个位置种植一块喂饱更赛牛的草地以及在第三个位置种植一块喂饱荷斯坦牛的草地。这使用了最小数量的草地并确保了所有奶牛都在她们喜欢的草地的 3 个位置以内。
测试点性质:
- 测试点 2-4 满足 N≤10。
- 测试点 5-8 满足 N≤40。
- 测试点 9-12 满足 N≤10^5。
供题:Mythreya Dharani
思路:
1. USACO的样例特别好,可以帮着梳理思路。这道题目其实需要整合的信息比较多,首先数据范围达到10^5,嵌套t就要10^6,因此嵌套循环就不要想了,必须往单层循环上边想。
2. 牛最多愿意移动k的距离去吃草,所以当前这块草坪最好是能够向左边覆盖k的距离,向右边也能覆盖k的距离,才能达到最好的效果。
3. 遍历牛的位置字符串,确定当前这头牛应该去哪里吃草,也就是确定放置草的位置。
#include <cstdio>
#include <iostream>
#include <string>
using namespace std;
int main(){
int t;
scanf("%d", &t);
while(t--){
int n, k;
scanf("%d%d", &n, &k);
string s;
string ans (n, '.'); //初始化字符串的方法
cin >> s;
int len = s.length();
int Ggrass = -k-1, Hgrass = -k-1, cnt = 0;//第一块草的位置标记是从0开始的,保证初始在-1才可
for (int i = 0; i < len; i++){
//如果当前位置是G牛,考虑上一块位置是否能覆盖它,如果不能,则要新增一块草地
if (s[i] == 'G' && i - Ggrass > k){
cnt++;
//接下来考虑这块草坪具体放在哪里
//首先如果想要草坪数量少,此时就要尽可能靠右边放置,这样当前草坪左边k和右边k距离的牛就都可以覆盖了
if(i + k >= n){
Ggrass = i;//如果超出n-1最后那个位置,就可以放在当前i的位置,因为i到n-1的牛也都可以来i这里
if(ans[Ggrass] == 'H')Ggrass--; //如果这个位置已经种了H草地,就要向左一个
} else {
Ggrass = i + k; //否则就要放在i向右距离k的位置
}
ans[Ggrass] = 'G'; //种上草地
}
//同理,如果当前位置是H牛,考虑上一块位置是否能覆盖它,如果不能,则要新增一块草地
else if(s[i] == 'H' && i - Hgrass > k){
cnt++;
if (i + k >= n){
Hgrass = i;
if (ans[Hgrass] == 'G')Hgrass--;
}else {
Hgrass = i + k;
}
ans[Hgrass] = 'H';
}
}
cout << cnt << "\n" << ans << "\n";
}
return 0;
}