DP - 区间DP - 枚举 - 前缀和
方法1:区间DP
设
f
[
i
]
[
j
]
f[i][j]
f[i][j] 为区间
[
i
,
j
]
[i,j]
[i,j] 中取反后所有元素的和,
s
[
i
]
=
∑
j
=
1
i
a
[
j
]
s[i]=\sum_{j=1} ^i a[j]
s[i]=∑j=1ia[j]
由于只是求和,不会有哪些乱七八糟的其他决策,我们考虑一个简单的转移
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
+
1
]
[
j
]
+
(
a
[
i
]
f[i][j]=max(f[i+1][j]+(a[i]
f[i][j]=max(f[i+1][j]+(a[i]
x
o
r
xor
xor
1
)
,
f
[
i
]
[
j
−
1
]
+
(
a
[
j
]
1),f[i][j-1]+(a[j]
1),f[i][j−1]+(a[j]
x
o
r
xor
xor
1
)
)
1))
1))
时间复杂度: O ( n 2 ) O(n^2) O(n2)
代码
#include<cstdio>
#include<iostream>
using namespace std;
const int Maxn=110,inf=0x3f3f3f3f;
int f[Maxn][Maxn],s[Maxn],a[Maxn];
int n,ans;
inline int read()
{
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0' && ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
return s*w;
}
int main()
{
// freopen("in.txt","r",stdin);
n=read();
for(int i=1;i<=n;++i)
{
a[i]=read(),s[i]=s[i-1]+a[i];
f[i][i]=a[i]^1;
ans=max(ans,f[i][i]+s[n]-a[i]); // 记得考虑区间大小为1的情况
}
for(int len=2;len<=n;++len)
{
for(int i=1;i+len-1<=n;++i)
{
int j=i+len-1;
f[i][j]=max(f[i+1][j]+(a[i]^1),f[i][j-1]+(a[j]^1));
ans=max(ans,f[i][j]+s[i-1]+s[n]-s[j]);
}
}
printf("%d\n",ans);
return 0;
}
方法2:暴力前缀和
既然只能挑一个区间取反,而
n
n
n 又小于等于
100
100
100 ,那么我们为什么不直接暴力枚举呢
时间复杂度
O
(
n
2
)
O(n^2)
O(n2)
其实不用前缀和都行,
O
(
n
4
)
O(n^4)
O(n4) 的算法都能过
#include<cstdio>
#include<iostream>
using namespace std;
const int Maxn=110,inf=0x3f3f3f3f;
int f[Maxn][Maxn],s[Maxn],a[Maxn];
int n,ans;
inline int read()
{
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0' && ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
return s*w;
}
int main()
{
// freopen("in.txt","r",stdin);
n=read();
for(int i=1;i<=n;++i)
a[i]=read(),s[i]=s[i-1]+a[i];
for(int i=1;i<=n;++i)
{
for(int j=i;j<=n;++j)
ans=max(ans,(j-i+1)-(s[j]-s[i-1])+s[i-1]+s[n]-s[j]);
}
printf("%d\n",ans);
return 0;
}