贪心 - Clam and Fish - 2020牛客暑期多校训练营(第三场)
题意:
T组测试用例,每组包括一个长度为n的字符串,由′0′、′1′、′2′、′3′组成。
0:当前阶段没有鱼、没有鱼饵。
1:当前阶段没有鱼、有鱼饵。
2:当前阶段有鱼、没有鱼饵。
3:当前阶段有鱼、有鱼饵。
每个阶段,只允许进行四种操作:
①、若有鱼饵,可以拿一份鱼饵。
②、若有一条鱼,可以直接捕一条鱼,无需花费。
③、在任何阶段,只要携带的鱼饵数量大于0,就可以花费一个鱼饵来捕捉一条鱼。
④、什么都不做。
计算最多能够捕捉多少条鱼。
输入:
首行:T(1≤t≤2.5×105),测试样例的数量。
接下来每组数据,包括n(1≤n≤2×106),字符串的长度。
再输入一行长度为n的字符串,表示n个阶段。
T组样例,n的总和不超过2×106
输出:
一个整数,表示捕捉到的鱼的数量的最大值。
示例1
输入
2
4
0103
1
1
输出
2
0
分析:
先逐个分析每个阶段:
阶段0:什么也没有,此时若携带的鱼饵数量大于0,必然是选择捕一条鱼。
阶段2:有鱼没鱼饵,那直接0代价捕鱼。
阶段3:有鱼有鱼饵,此时选择捕鱼是最优的。因为即使增加鱼饵,也至多使得鱼的数量增加1。
阶段1:没鱼有鱼饵,未必要选择增加鱼饵。因为鱼饵最后可能会剩余,所以也可以选择捕鱼。
因此我们发现,阶段0、2、3的操作都是确定的,只有阶段1需要考虑是选择增加鱼饵还是捕鱼。
什么样的情况下选择在阶段1捕鱼呢?
假设设当前所处的位置i处在阶段1,若i以后存在的阶段0的数量要少于当前拥有的鱼饵数量,就无需再增加鱼饵了。
因为再增加鱼饵,最后必然会有鱼饵剩余。所以我们在这种情况下选择捕鱼。
捕完一条鱼后,鱼饵数量将与i以后的阶段0的数量相等。按照这样的贪心策略,就能够得到最优解。
由于我们要快速查询第i位以后的所有阶段中,阶段0的数量。故我们要先预处理数组cnt0[i]。
时间复杂度:
O(n)
注意:
T组测试样例n的总和不超过2×106,
对于每组测试数据,cnt0数组无需全部初始化为0,仅需初始化前n位即可。
用memset会超时!
这里T了好几发。
代码:
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=2e6+10;
int T,n;
char str[N];
int cnt0[N];
int main()
{
cin>>T;
while(T--)
{
for(int i=0;i<=n+2;i++) cnt0[i]=0;
int res=0,ecnt=0;
scanf("%d",&n);
scanf("%s",str);
for(int i=n-2;i>=0;i--)
{
cnt0[i]=cnt0[i+1];
if(str[i+1]=='0') cnt0[i]++;
}
for(int i=0;i<n;i++)
{
if(str[i]=='0' && ecnt) {res++;ecnt--;}
else if(str[i]=='2'||str[i]=='3') res++;
else if(str[i]=='1')
{
if(ecnt>cnt0[i]) {res++;ecnt--;}
else ecnt++;
}
}
printf("%d\n",res);
}
return 0;
}