1. 程式人生 > >Codeforces 811C Vladik and Memorable Trip (區間異或最大值)【線性DP】

Codeforces 811C Vladik and Memorable Trip (區間異或最大值)【線性DP】

所有 一個 true sin %d 如果 不同 esp ace

<題目鏈接>

題目大意:

給你n個數,現在讓你選一些區間出來,對於每個區間中的每一種數,全部都只能出現在這個區間。 每個區間的價值為該區間不同的數的異或值之和,現在問你這n個數最大的價值是多少。

解題分析:
剛開始真的是沒有什麽想法。因為要同一種的所有數只能出現在同一區間,所以我們先對這$n$個數進行預處理,得到他們每種數的最左邊的坐標和最右邊的坐標。然後就是暴力枚舉最後一個異或的區間進行更新,用dp值來記錄。

$dp[i]$表示$[1,i]$中異或值之和的最大值。

不難想到,我們暴力枚舉最後一個異或的區間,設區間左端點為$j$,區間端點為$i$。

轉移方程就是:$dp[i]=max(dp[i],dp[j-1]+res)$ res表示$[j,i]$區間所有數的異或值

#include <bits/stdc++.h>
using namespace std;

const int N = 5e3+5;
int n,L[N],R[N];
int arr[N],dp[N],vis[N];

int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        scanf("%d",&arr[i]);
        if(!L[arr[i]]){
            L[arr[i]]=i;   //記錄最左邊的坐標
        }R[arr[i]]=i;      //
記錄最右邊的坐標 } /*for(int i=1;i<=n;i++){ dp[i]=dp[i-1]; //首先默認這個數不取 if(R[arr[i]]==i){ //如果這個點是最右邊的數 memset(vis,0,sizeof(vis)); int res=0; bool fp=true; for(int j=L[arr[i]];j<=i;j++){ //這個區間的所有不相等的數的異或值 if(R[arr[j]]>i || L[arr[j]]<L[arr[i]]){ fp=false; break; } if(!vis[arr[j]]) res^=arr[j],vis[arr[j]]++; } if(fp)dp[i]=max(dp[i],dp[L[arr[i]]-1]+res); } //WA 29的寫法 }
*/ for(int i=1;i<=n;i++){ dp[i]=dp[i-1]; memset(vis,0,sizeof(vis)); int res=0,left=i; for(int j=i;j>=1;j--){ //枚舉的區間左端點 if(!vis[arr[j]]){ //枚舉1~i區間的所有點 if(R[arr[j]]>i)break; left=min(left,L[arr[j]]); //得到這個區間內所有元素的最左邊的端點坐標 res^=arr[j]; vis[arr[j]]++; } if(left>=j)dp[i]=max(dp[i],dp[j-1]+res); //如果大於枚舉的區間左值,則成立 } } cout<<dp[n]<<endl; }

Codeforces 811C Vladik and Memorable Trip (區間異或最大值)【線性DP】