分类讨论的做法,贪心的进行一个放一个的操作可以大大减少需要分类讨论的情况,但是有两种情况需要特别注意:
即两个0连在一起替换的情况,还有三个子串连成一个这种的情况需要特别的进行讨论.
这两种情况,贪心的做法很有可能出现错误,比如说三个子串连成一个这种,假如用贪心的做法,放一个最优再放一个当前最优,比如这样的数据:
22
1110111000001110110111
会导致出错,因为贪心的选择不会将那三个子串连成一个子串,导致不是最优解。
下面贴上代码
大家有什么建议或者问题可以在评论区回复我
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
char a[1000010];
int b[1000010];
int he(int x)
{
int fl=(x*(x+1))/2;//求和函数,比较方便
return fl;
}
void solve()
{
int n;cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
int sum=0;
for(int i=1;i<=n;i++)
{
if(a[i]=='0') sum++;
}
if(sum<=2)
{
cout<<he(n)<<endl;//特判的情况
return;
}
sum=0;
int now=0;
for(int i=1;i<=n;i++)
{
if(a[i]=='1')
{
now++;
}
else
{
b[i-1]=now;
now=0;
}
}
if(now!=0)
{
b[n]=now;
now=0;
}
//上面的是每个1子串末尾位置的代表的个数
for(int i=1;i<=n;i++)
{
if(b[i]!=0)
{
sum=sum+he(b[i]);
}
}
//这里把初始的和求一遍
now=0;
for(int i=n;i>=1;i--)
{
if(a[i]=='1')
{
now++;
}
else
{
b[i+1]=now;
now=0;
}
}
if(now!=0)
{
b[1]=now;
now=0;
}
//这里是倒着遍历,把每个1子串最开始的地方标注上这个1子串的长度
int ans=0;//最后的答案
// **********************************************
int fl1=0;//这里是求只替换一个0时候的情况
for(int i=1;i<=n;i++)
{
if(a[i]=='0')
{
int duo=he(b[i-1])+he(b[i+1]);//不把0变成1时候的贡献
int pan=he(b[i-1]+1+b[i+1]);//把0变成1时候的贡献
fl1=max(fl1,(pan-duo));
}
}
// **********************************************
int fl2=0;//这里是求两个0连起来的情况
for(int i=1;i<=n-1;i++)
{
if(a[i]=='0'&&a[i+1]=='0')
{
int duo=he(b[i-1])+he(b[i+2]);
int pan=he(b[i-1]+2+b[i+2]);
fl2=max(fl2,(pan-duo));
}
}
// **********************************************
int fl3=0;//这里是求将三个子串连成一个情况
int sig1=0;//唉,赛时就是这种情况没写上,遗憾银首
int sig1z;
int sig1y;
for(int i=1;i<=n;i++)
{
if(a[i]=='0')
{
if(sig1==1)
{
int pan=he(sig1z+1+sig1y+1+b[i+1]);
int duo=he(sig1z)+he(sig1y)+he(b[i+1]);
fl3=max(fl3,(pan-duo));
sig1=1;
sig1z=(b[i-1]);
sig1y=(b[i+1]);
}
else
{
sig1=1;
sig1z=(b[i-1]);
sig1y=(b[i+1]);
}
}
}
// **********************************************
int dai=0;//再这种情况下总共的答案
int fl4=0;//这里是进行贪心求解,先放一个最优的进去,再将这个0换成1再进行一次预处理,再放一个最优的进去
int xb=0;//下标
for(int i=1;i<=n;i++)
{
if(a[i]=='0')
{
int duo=he(b[i-1])+he(b[i+1]);
int pan=he(b[i-1]+1+b[i+1]);
if((pan-duo)>fl4)
{
fl4=(pan-duo);
xb=i;//得到下标
}
}
}
a[xb]='1';//将其更改为1
for(int i=1;i<=n;i++) b[i]=0;//将前后缀数组清空
//再进行一次前后缀处理
now=0;
for(int i=1;i<=n;i++)
{
if(a[i]=='1')
{
now++;
}
else
{
b[i-1]=now;
now=0;
}
}
if(now!=0)
{
b[n]=now;
now=0;
}
for(int i=1;i<=n;i++)
{
if(b[i]!=0)
{
dai=dai+he(b[i]);
}
}
now=0;
for(int i=n;i>=1;i--)
{
if(a[i]=='1')
{
now++;
}
else
{
b[i+1]=now;
now=0;
}
}
if(now!=0)
{
b[1]=now;
now=0;
}
//再进行一次前后缀处理
int xwc=0;//这里再将第二次放的最优贡献记录下来
for(int i=1;i<=n;i++)
{
if(a[i]=='0')
{
int duo=he(b[i-1])+he(b[i+1]);
int pan=he(b[i-1]+1+b[i+1]);
xwc=max(xwc,(pan-duo));
}
}
dai=dai+xwc;
fl4=dai-sum;//将新得到的和减去之前的和,得到fl4的总贡献
// **********************************************
ans=sum+max( max(fl1,fl2) , max(fl3,fl4 ) );//ans是sum+最大的贡献
cout<<ans<<endl;
}
signed main()
{
int T=1;
// cin>>T;
while(T--) solve();
return 0;
}