CodeForces - 1372D - Omkar and Circle 前缀和(dp)
题意:给一个奇数长度的数组,操作是选择一个位置,删除其相邻的数,并将该位置的数赋值为相邻数的和,求最终剩下的一个数字最大为多少
注意:位置1和位置n算相邻
思路及问题转化:选择某位置,表面上看删除的是相邻的两个数,但是其实总和 S S S减少的是这个位置的数,于是这个问题可以转化为:删除 n / 2 n/2 n/2个不相邻的数,剩下的数字和最大
如何去利用前缀和:在一个奇数长度的数组中删去 n / 2 n/2 n/2个数,剩余数字 n / 2 + 1 n/2+1 n/2+1个,共产生空隙 n / 2 个 n/2个 n/2个,每个空隙最小为 1 1 1,故有且仅有一个空隙为 2 2 2的地方,枚举这一个空隙为2的右端数字,将其确定下来之后,其余“应当删除”的位置都确定了,计算他们的总和并取最小就行
可以先手动算几个:
d
p
[
i
]
[
0
]
dp[i][0]
dp[i][0]表示
[
1
i
]
[1~i]
[1 i]的奇数位置前缀和
d
p
[
i
]
[
1
]
dp[i][1]
dp[i][1]表示
[
1
i
]
[1~i]
[1 i]的偶数位置前缀和
假设数组 1 2 3 4 5 6 7
删除位置 | 组成空隙2的两个位置 | 数组删除情况 | 用前缀和表示删除数字的总和 |
---|---|---|---|
1 | 6,7 | d p [ n − 2 ] [ 0 ] dp[n-2][0] dp[n−2][0] | |
2 | 7,1 | 1 | d p [ n − 1 ] [ 1 ] dp[n-1][1] dp[n−1][1] |
3 | 1,2 | 1 2 | d p [ i − 3 ] [ 1 ] + d p [ n ] [ 0 ] − d p [ i − 1 ] [ 0 ] dp[i-3][1]+dp[n][0]-dp[i-1][0] dp[i−3][1]+dp[n][0]−dp[i−1][0] |
4 | 2,3 | d p [ i − 3 ] [ 0 ] + d p [ n ] [ 1 ] − d p [ i − 1 ] [ 1 ] dp[i-3][0]+dp[n][1]-dp[i-1][1] dp[i−3][0]+dp[n][1]−dp[i−1][1] | |
5 | 3,4 | 1 | d p [ i − 3 ] [ 1 ] + d p [ n ] [ 0 ] − d p [ i − 1 ] [ 0 ] dp[i-3][1]+dp[n][0]-dp[i-1][0] dp[i−3][1]+dp[n][0]−dp[i−1][0] |
6 | 4,5 | d p [ i − 3 ] [ 0 ] + d p [ n ] [ 1 ] − d p [ i − 1 ] [ 1 ] dp[i-3][0]+dp[n][1]-dp[i-1][1] dp[i−3][0]+dp[n][1]−dp[i−1][1] | |
7 | 5,6 | 1 | d p [ i − 3 ] [ 1 ] + d p [ n ] [ 0 ] − d p [ i − 1 ] [ 0 ] dp[i-3][1]+dp[n][0]-dp[i-1][0] dp[i−3][1]+dp[n][0]−dp[i−1][0] |
代码:
int n;
ll a[maxn],dp[maxn][2],cnt=0,t;
int main()
{
scanf("%d",&n);
rep(i,1,n)
{
scanf("%lld",&a[i]);
cnt+=a[i];
dp[i][1]=dp[i-1][1];
dp[i][0]=dp[i-1][0];
if (i%2==1)dp[i][0]+=a[i];
else dp[i][1]+=a[i];
}
ll ans=min(dp[n-2][0],dp[n-1][1]);
//dp[n-2][0]对应的是删除位置1
//dp[n-1][1]对应的是删除位置2
rep(i,3,n)//枚举删除位置
{
if (i%2==1)
{
t=dp[n][0]-dp[i-1][0];//右
t+=dp[i-3][1];//左
}
else
{
t=dp[n][1]-dp[i-1][1];//右
t+=dp[i-3][0];//左
}
ans=min(ans,t);
}
WW(cnt-ans);
return 0;
}