题解:P16350 「Gensokyo OI Round 1」隙间插曲
这题可以随便
考虑随便刻画一下,容易删除队头的影响一定是一个前缀,队尾则是后面的一些元素。
后者看着好难啊!
我们假设已经确定最终序列删除了哪些,尝试判断是否合法。
发现对于没删除的数,似乎包含一些信息,因为我们删除不能跨过去。
于是我们考虑每个删除的连续段。
我们设 (,否则是 ),发现这个段必须形成一个合法括号序列(如果不合法,要么 ) 不足,此时做不到,或者 ) 太多,这时候我们删掉多的部分,那些多的部分肯定是投给删队头了,然后还是合法括号序列)。
进一步的,我们连续段还可以分割,划分成最多的合法括号序列,于是考虑从
于是我们就能高高兴兴的 DP 了,设
统计就每次算完
#include<bits/stdc++.h>
using namespace std;
#define int long long
int dp[1009][1009];
int a[1000009];
int s[1000009];
signed main(){
int n;
cin>>n;
int ze;
ze=0;
for(int i=1;i<=n;i++){
cin>>a[i];
s[i]=s[i-1]+(a[i]==0?-1:1);
ze+=(a[i]==0);
}
for(int i=0;i<=n+1;i++){
for(int j=0;j<=n;j++){
dp[i][j]=1e18;
}
}
dp[n+1][0]=0;
int ans;
ans=0;
for(int i=1;i<=n;i++){
if(!ze){
break;
}
if(a[i]){
ze--;
ans+=a[i];
}
}
for(int i=n;i>=1;i--){
if(a[i]){
int c,ss;
c=ss=0;
for(int z=i;z<=n;z++){
if(a[z]){
c++;
ss+=a[z];
}else{
c--;
}
if(c==0){
for(int j=0;j<=n;j++){
dp[i][j]=dp[z+1][j]+ss;
}
break;
}
}
for(int j=0;j<=n;j++){
dp[i][j]=min(dp[i][j],dp[i+1][j]);
}
}else{
for(int j=1;j<=n;j++){
dp[i][j]=dp[i+1][j-1];
}
}
int sum;
sum=0;
for(int j=1;j<i;j++){
sum+=a[j];
}
sum+=dp[i][s[i-1]];
ans=min(ans,sum);
}
int sum;
sum=0;
for(int i=1;i<=n;i++){
sum+=a[i];
}
cout<<sum-ans;
return 0;
}