当前位置: 代码迷 >> 综合 >> CF #407 DIV2 C题 区间dp
  详细解决方案

CF #407 DIV2 C题 区间dp

热度:114   发布时间:2023-09-27 21:44:16.0

题目链接

CF #407 DIV2 C题 区间dp

解析:求最大的区间和问题,联系到基本的区间子段和,但是这里需要注意的是,区间的和与区间[l,r) 中的l 相关,

理所当然的解法是对数组先做绝对值差分,但是我们会发现,如果当前位置的值是abs(a[i] - a[i+1]),假如这个位置存在于某个区间[l,r)中,那么只有当i%2 == l%2的时候,才会取的原值,否则需要取相反数。即保证一个奇偶性。

那么 对于dp[i]假如认为这个状态是以差分i结尾的最大值,那么联系最大子段和,这个状态由前面的dp[i-1] (+ 或者-)abs(a[i]-a[i+1])或者就是abs(a[i]-a[i+1])得到,但是问题来了,怎么判断是+还是-。那么就和dp[i-1]是以奇数开始 还是偶数开始的区间相关了。所以接下来就很容易找出dp方程。

dp1[i]表示的是以奇数开始的区间到第i个差分的最大子段和。 

dp2[i]表示的是以偶数开始的区间到第i个差分的最大子段和。

所以dp方程:

if(i%2){dp1[i] = max(dp1[i-1] + v[i],v[i]);dp2[i] = max(dp2[i-1] - v[i],0LL);
}else{dp1[i] = max(dp1[i-1] - v[i],0LL);dp2[i] = max(dp2[i-1] + v[i],v[i]);
}

此处判断i的奇偶性就是为了能够判断出i和区间左边界l的奇偶性是否相同。

但是我们发现dp方程里面有个0,为什么不是-v[i]这个差分呢?因为假如当前i是奇数,那么对于l是偶数的dp2[i]来说,假如dp2[i-1] - v[i]的值相对v[i]较小,那么按理应该取-v[i],但是此处如果取的-v[i],表示的是dp2[i]的最大值的起点是i,但是i是奇数而dp2[i]的起点应该是偶数,这与开始设计的dp意义相违背,所以此处取0符合逻辑。

这个一维的线性dp可以做一个内存优化。

以下是dp数组的ac代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll n,a[100005],v[100005],dp1[100005],dp2[100005],ans =-1e17;
ll max(ll a,ll b){return a>b?a:b; 
}
int main(){memset(dp1,0,sizeof(dp1));memset(dp2,0,sizeof(dp2));scanf("%lld",&n);for(int i = 1;i<=n;i++){scanf("%lld",&a[i]);v[i-1] = abs(a[i] - a[i-1]);}for(int i = 1;i<n;i++){if(i%2){dp1[i] = max(dp1[i-1] + v[i],v[i]);dp2[i] = max(dp2[i-1] - v[i],0LL);}else{dp1[i] = max(dp1[i-1] - v[i],0LL);dp2[i] = max(dp2[i-1] + v[i],v[i]);}ans = max(ans,max(dp1[i],dp2[i]));	}printf("%lld",ans);
}

内存优化后的ac代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll n,a[100005],dp1 = 0,dp2 = 0,ans =-1e17;
ll max(ll a,ll b){return a>b?a:b; 
}
int main(){scanf("%lld",&n);for(int i = 1;i<=n;i++){scanf("%lld",&a[i]);a[i-1] = abs(a[i] - a[i-1]);}for(int i = 1;i<n;i++){if(i%2){dp1 = max(dp1 + a[i],a[i]);dp2 = max(dp2 - a[i],0LL);}else{dp1 = max(dp1 - a[i],0LL);dp2 = max(dp2 + a[i],a[i]);}ans = max(ans,max(dp1,dp2));	}printf("%lld",ans);
}

复杂度的比较:

CF #407 DIV2 C题 区间dp

  相关解决方案