题目
题意
给定一个长度为n的数组a(|a[i]|<=2)。你可以删除数组的前缀和后缀,使得剩余的子数组的乘积最大。
给出删除的方案。一个空的数组乘积为 1。
思路
因为题目说的空的数组的乘积为1,所以最后的剩余子数组中一定没有0这个数。
所以我们可以通过0来分割整个数组。
[…0…0…0…]
对于一个不包含0的区间,我们考虑负数的个数。
如果负数的个数为偶数,整个区间都拿来更新答案。
如果负数的个数为奇数,我们我们希望区间中的负数个数为偶数,在此基础上,区间中的数越多越好:
找到第一个负数的位置id ,用区间[l,id-1],[id+1,r] 来更新答案。
找到最后一个负数的位置id, 用区间[l,id-1],[id+1,r]来更新答案。
如何更新答案?因为我们保证了区间中没有0,而且负数的个数为偶数个,所以影响最后乘积结果的只有绝对值为2的数的个数。
我们用cnt 来记录2的个数的最大值。
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
const int inf=0x3f3f3f3f;
typedef long long ll;
int a[maxn];
char cot[maxn];
int n;
int cnt;
int ans_x,ans_y;
void upd(int l,int r)
{
if(l>r)
return;
int k=0;
for(int i=l;i<=r;i++)
{
if(abs(a[i])==2)
k++;
}
if(k>cnt)
cnt=k,ans_x=l-1,ans_y=n-r;
}
int cot_sign(int l,int r)
{
int k=0;
for(int i=l;i<=r;i++)
{
if(a[i]<0)
k++;
}
return k;
}
void cal(int l,int r)
{
int sign=cot_sign(l,r);
if(sign%2==0)
upd(l,r);
else
{
int id=l;
while(a[id]>0)id++;
upd(l,id-1);
upd(id+1,r);
id=r;
while(a[id]>0)id--;
upd(l,id-1);
upd(id+1,r);
}
}
void solve()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
a[n+1]=0;
ans_x=n,ans_y=0,cnt=0;
for(int i=1,j=1;i<=n+1;i++)
{
if(a[i]==0)
{
if(i-1>=j)
cal(j,i-1);
j=i+1;
}
}
cout<<ans_x<<" "<<ans_y<<endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin>>t;
while(t--)
{
solve();
}
}