Codeforces Round #416 (Div. 2) C. Vladik and Memorable Trip(DP)
题目链接:
题意:给你一个数字串,让你把他分开成几个不重叠的子数字串,每个子数字串里的每一个数字需要全部在这个子数字串的区间内部,比如5,3,2,3,2,1,3对于这个数列我们取2,3,2,1作为一个子数字串区间是不对的,因为并不是所有的3都在这个区间里面。如果我们取3,2,3,2,1,3这样的区间就是对的,因为这个区间里面所有的不同的数都在这个区间里面,我们对每个区间的数不同的数异或一遍。然后把所有子数字串区间的异或和相加求这个和的最大值。
思路:
1.我们要知道我们所选区间内部是否包含了这个数在整个数字串里面的全部数量。我们就需要知道每一个数在整个子串里面第一个出现的位置,和最后一个出现的位置,这就需要预处理一下。
2.我们枚举i作为序列的结尾,然后反向枚举j作为序列的开头。如果[j,i]区间内包含了所有[j,i]内的所有的数在整个数字串中的全部数量。那么我们就可以 dp[i]=max(dp[i],dp[j-1]+sum);
3.否则我们善意的认为dp[i]=dp[i-1]
4.一路维护dp[i]的最大值。
代码:
//Author: xiaowuga#include#define maxx INT_MAX#define minn INT_MIN#define inf 0x3f3f3f3fconst long long N= 5001;using namespace std;typedef long long L;int main() { ios::sync_with_stdio(false);cin.tie(0); int n; cin>>n; int a[N]; for(int i=1;i<=n;i++) cin>>a[i]; int fir[N]={ 0},las[N]={ 0}; for(int i=1;i<=n;i++) las[a[i]]=i;//最后一个出现地方 for(int i=n;i>=1;i--) fir[a[i]]=i;//最后一个出现的地方 int vis[N]; int dp[N]={ 0}; for(int i=1;i<=n;i++){ dp[i]=dp[i-1]; int sum=0; memset(vis,0,sizeof(vis));//去重 int st=fir[a[i]]; for(int j=i;j>=1;j--){ if(vis[a[j]]==0){ if(las[a[j]]>i) break;//如果这个区间没有包含所有的这个数 st=min(st,fir[a[j]]);//[j,i]区间内的元素在数组中第一个元素的出现最早的下标 sum^=a[j];//[j,i]区间内所有不同的元素的异或的结果 vis[a[j]]=1; } if(j<=st) dp[i]=max(dp[i],dp[j-1]+sum); } } cout< <