Codeforces Round #407 (Div. 2) C 数学 + DP

来源:互联网 发布:新浪云怎么绑定域名 编辑:程序博客网 时间:2024/05/21 20:26

题目链接

思路:
首先我们可以将每相邻两项的差的绝对值算出来,然后按顺序形成一个新的数列:
如样例

1 4 2 3 1   -->    3 2 1 2

记新数组为 a
观察公式,选取某一起点元素u,终点元素v,则:

f(l,r) = a[u] - a[u+1] + a[u+2] - a[u+3] ... a[v]

即正负号依此出现,呈交错性。

因为起点u和终点v可以任意选,则原问题转化为:
给你两个数组,求任意连续区间和的最大值

对于样例,即为:

求 a1: 3 -2 1 -2 和 a2: -3 2 -1 2 中任意连续区间和的最大值。

然后就变成了一个经典的问题。
朴素的枚举起点终点 或者 枚举区间长度 都是O(n^2) 此题果断超时没商量。

此时有两种写法进行选择:
一 .DP: 对于某一个元素 i,其作为终点时连续区间和的最大值dp[i]满足 :

dp[i]=max(0,dp[i1])+a[i]

二.处理前缀和 + 动态维护最小前缀和:

因为对于某一个元素 i,其作为终点时连续区间和的最大值dp[i]满足:

dp[i]=sum[i]min(sum[j])(0=<j<i<=n)

其中sum为前缀和,sum[j]为 i 之前最小的前缀和。

for(int i=1 ;i<=n ;i++){    // sum[0] = 0    sum[i] = sum[i-1] + a[i];}for(int i=1 ;i<=n ;i++){    ans = max(ans,sum[i] - Min);    Min = min(Min,sum[i]);}

然后本蒟蒻选的第一种DP写法:

#include<bits/stdc++.h>using namespace std;typedef long long ll;const int A = 1e5 + 100;int a[A];ll v_1[A],v_2[A];ll dp_1[A],dp_2[A];int main(){    int n;    scanf("%d",&n);    for(int i=1 ;i<=n ;i++){        scanf("%d",&a[i]);    }    int now = 1;    for(int i=1 ;i<n ;i++){        v_1[i] = now * abs(a[i+1] - a[i]);        v_2[i] = -now * abs(a[i+1] - a[i]);        now = -now;    }    ll ans = -1;    dp_1[0] = dp_2[0] = 0;    for(int i=1 ;i<n ;i++){       dp_1[i] = max((ll)0,dp_1[i-1]) + v_1[i];       dp_2[i] = max((ll)0,dp_2[i-1]) + v_2[i];       ans = max(ans,dp_1[i]);       ans = max(ans,dp_2[i]);    }    printf("%I64d\n",ans);    return 0;}
0 0
原创粉丝点击