[NOMURA Programming Competition 2020]E - Binary Programming

Portal
先考虑倒着操作,也就是变成每次删除后统计贡献,最后加上原序列的贡献即可。由于显然可以将删除顺序看成一个排列,那么就考虑对这个排列进行 d p dp dp

也就是设 f ( i ) f(i) f(i)表示仅考虑前 i i i个字符时的最大权值(不考虑原序列的贡献),转移到 f ( i + 1 ) f(i+1) f(i+1)就是新增第 i + 1 i+1 i+1个字符。当新增字符为 1 1 1时,通过贪心可以发现一定是最后删除最优,所以它新增的贡献就为 ⌈ i 2 ⌉ \lceil\frac{i}{2}\rceil 2i。而当新增字符为 0 0 0时,贡献其实就是在删除过程中奇数位置上最多的 1 1 1的个数,考虑维护它。

具体实现还是 d p dp dp,设 f 0 f_0 f0表示删除了偶数个位置后奇数位置上最多的 1 1 1的个数, f 1 f_1 f1则为删除了奇数个位置后。那么当新增字符为 1 1 1时,显然为让 f i d ( i ) + + f_{id(i)}++ fid(i)++(这个操作其实是对的,因为显然时时都满足 ∣ f 0 − f 1 ∣ ≤ 1 |f_0-f_1|\leq1 f0f11。其中 i d ( i ) id(i) id(i)表示 i i i的奇偶性),而当为 0 0 0时,就是让 f 0 = f 1 = m a x ( f 0 , f 1 ) f_0=f_1=max(f_0,f_1) f0=f1=max(f0,f1),因为多删除一个 0 0 0后删除的位置个数的奇偶性就改变了。由上 0 0 0的贡献就可以得到了,就为 m a x ( f 0 , f 1 ) max(f_0,f_1) max(f0,f1)

那么这道题就做完了,时间复杂度为 O ( n ) O(n) O(n),可以轻松通过本题。

#include
#include
#include
#include
#include
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9')x=x*10+ch-'0',ch=getchar();
	return x*f;
}
int stack[20];
inline void write(int x)
{
	if(x<0){putchar('-');x=-x;}
	if(!x){putchar('0');return;}
	int top=0;
	while(x)stack[++top]=x%10,x/=10;
	while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x),putchar(' ');}
inline void pr2(int x){write(x),puts("");}
char ss[200010];
int main()
{
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    scanf("%s",ss+1);int n=strlen(ss+1);
    long long ans=0;
    int f0=0,f1=0;
    for(int i=1;i<=n;i++)
	{
		if(ss[i]=='1')
		{
			ans+=(i+1)/2;
			if(i&1)f0++;
			else f1++;
		}
		else {int ul=max(f0,f1);ans+=max(f0,f1),f0=f1=ul;}
	}
    printf("%lld\n",ans);
    return 0;
}

你可能感兴趣的:(dp,贪心)