CF201C

最优的方案应该是先往一个方向走,然后走回来,再往另一个方向走不回来。考虑用 dp 模拟这个过程。设 \(f_{i,0/1}\) 表示从第 \(i\) 个点出发往左走,不一定/一定回到 \(i\) 号点的最大次数,则有转移:

\[\begin{array}{l} f_{i,1}=f_{i-1,1}+a_{i-1}-[2 \nmid a_{i-1}]\\ f_{i,0}=\max\left\{ \begin{array}{l} f_{i-1,0}+a_{i-1}-[2 \mid a_{i-1}]\\ f_{i-1,1}+a_{i-1}\\ f_{i,1}\\ \end{array} \right. \end{array} \]

转移的意义分别是:

  • \(i\) 出发,把 \(i-1\) 左边走完以后,不停在 \(i-1\)\(i\) 之间移动,最终在 \(i\) 结束。
  • \(i\) 出发,先在 \(i-1\)\(i\) 之间移动,最终到 \(i-1\),走 \(i-1\) 左边的点。
  • \(i\) 出发,把 \(i-1\) 左边走完以后,不停在 \(i-1\)\(i\) 之间移动,最终在两者任意一个结束。
  • 可以回到 \(i\)

同理维护出向右走的最大次数。时间复杂度 \(O(n)\)

#include<iostream>
#include<cstdio>
#define int long long
#define N 100010
using namespace std;
int n,a[N],f[N][2],g[N][2],ans;
signed main(){
    cin>>n;
    for(int i=1;i<n;i++)
        cin>>a[i];
    for(int i=2;i<=n;i++){
        if(a[i-1]>1)f[i][1]=f[i-1][1]+a[i-1]/2*2;
        f[i][0]=max(f[i][1],max(f[i-1][0]+a[i-1]-(a[i-1]%2^1),f[i-1][1]+a[i-1]));
    }
    for(int i=n-1;i>=1;i--){
        if(a[i]>1)g[i][1]=g[i+1][1]+a[i]/2*2;
        g[i][0]=max(g[i][1],max(g[i+1][0]+a[i]-(a[i]%2^1),g[i+1][1]+a[i]));
    }
    for(int i=1;i<=n;i++){
        ans=max(ans,f[i][0]+g[i][1]);
        ans=max(ans,f[i][1]+g[i][0]);

    }
    cout<<ans;
    return 0;
}
posted @ 2025-09-12 19:42  FormulaOne  阅读(8)  评论(0)    收藏  举报