Educational Codeforces Round 152 (Rated for Div. 2)
D. Array Painting
题目链接
题意:
给你一个由 n n n 个整数组成的数组,其中每个整数都是 0 0 0 、 1 1 1 或 2 2 2 。最初,数组中的每个元素都是蓝色的。
您的目标是将数组中的每个元素涂成红色。为此,您可以执行两种类型的操作:
- 支付一枚硬币,选择一个蓝色元素并将其涂成红色;
- 选择一个不等于 0 0 0 的红色元素和一个与之相邻的蓝色元素,将所选的红色元素减少 1 1 1 ,并将所选的蓝色元素涂成红色。
要实现目标,你最少需要花费多少金币?
思路:
如果我们把连续的非零部分看作一个部分,那么如果这个部分里只有1,它就只能吸收一边的一个0。如果这个部分有2,那么它可以吸收两边各一个0。这一个区间只花1块钱。
假设我们规定一个区间只吸收一个0的话,先吸收左边的,没有再吸收右边的,所以用双指针从左到右看。其实无外乎四种情况:
- 0 非零区间 0
- 0 非零区间
- 非零区间 0
- 0 0
第二种情况其实是不存在的,因为后面没0的话可以一直走到结尾,所以我们把第 n + 1 n+1 n+1 个数看成 0 0 0 就可以了,这样就不存在第二种情况了。所以就是如果 l l l 是个 0 0 0 就让 r r r 从 l + 1 l+1 l+1 向后找第二个 0 0 0,如果中间的非零区间有 2 2 2,就吃掉后面的 0 0 0 , l l l 指向 r + 1 r+1 r+1。如果没有 2 2 2 就指向 r r r。如果l是个非0数,也是向后找,不过后面的 0 0 0 一定可以吃掉,直接指向 r + 1 r+1 r+1 即可。
code:
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn=2e5+5;
int n,a[maxn],ans;
int main(){
cin>>n;
for(int i=1,t;i<=n;i++)
cin>>a[i];
bool f;
for(int l=1,r,maxx;l<=n;l=r){
maxx=0;
r=l+(f=a[l]==0);
while(a[r]!=0)
maxx=max(maxx,a[r++]);
if(maxx==2 || !f)r++;
ans++;
}
cout<<ans;
return 0;
}
思路2:
队友的思路,把找区间的操作和累加操作拆开,就是先缩点,再找答案。把上面说的非零区间缩成一个数,有2的区间缩成2,没2的缩成1,之后和上面的想法一样,先吃掉左边的0,吃不到或者能吃两个就把右边的0吃掉,被吃掉的0标记为-1。最后数一下非-1的数的个数就好了。
code:
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn=2e5+5;
int n,a[maxn],ans;
int b[maxn],cnt;
int main(){
cin>>n;
for(int i=1,t;i<=n;i++)
cin>>a[i];
for(int i=1,maxx;i<=n;i++){//缩点
if(a[i]==0)b[++cnt]=0;
else {
maxx=a[i];
while(a[i+1]!=0){
i++;
maxx=max(maxx,a[i]);
}
b[++cnt]=maxx;
}
}
b[0]=b[cnt+1]=-1;
for(int i=1;i<=cnt;i++){
if(b[i]==1){
if(b[i-1]==0)b[i-1]=-1;
else b[i+1]=-1;
}
if(b[i]==2){
b[i-1]=b[i+1]=-1;
}
}
for(int i=1;i<=cnt;i++)
if(~b[i])
ans++;
cout<<ans;
return 0;
}