原题链接:http://codeforces.com/contest/811/problem/C
题意:将数组中的连续数字连成若干个“线段”(或者不连),其实就是区间。区间必须满足对于其中的任意数字,能够覆盖数组中的所有相等数字,比如对数组:1, 2, 5, 2 ,5, [2, 5 ,2 ,5]是满足条件的区间,而[2, 5, 2]不是,因为它并没有包含所有的5.
题目求不相交的满足条件的区间内不同数字的异或和的最大值。
思路:这是一道普通的DP题,类似最长上升子序列,先求出每个数字的最长区间(左右端点),再定义dp[i]为左端点为1, 长度为i的区间所能得到的最大异或和。则dp[i]= dp[j-1]+res, j是被包含在[1, i]的满足条件的区间的左端点,res是对应的异或和。这么dp下去就好了。
看别人的题解时自己尝试了一下,dp部分是枚举所有满足条件的区间,WA了。思考后发现,这么做有个错误,事实上第一步求出来的区间不一定是对的,没有保证在区间内和区间外同时出现同一个数字(也就是不满足条件的)。而枚举数字的位置可以在上述情况发生(或者说,对应区间不在[1, i]内)时停止枚举。具体实现见代码。
AC代码:
#include<iostream> #include<cstring> #include<cstdio> using namespace std; const int MAXN = 5005; int num[MAXN],dp[MAXN]; struct Edge{ int l,r; }edge[MAXN]; bool vis[MAXN]; int main() { int n,m; scanf("%d",&n); dp[0]=0; for(int i=0;i<MAXN;i++) edge[i].l=edge[i].r=0; for(int i = 1 ; i <= n ; ++i) { scanf("%d",&num[i]); m=num[i]; if(!edge[m].l)edge[m].l = i; edge[m].r = i; } for(int i = 1 ; i <= n ; ++i) { dp[i] = dp[i-1]; memset(vis,0,sizeof(vis)); int st = edge[num[i]].l; int res = 0; for(int j = i ; j >= 1 ; --j) { if(!vis[num[j]]) { if(edge[num[j]].r>i)break; //对应区间不在[1, i]内,停止枚举 st = min(st,edge[num[j]].l); res^=num[j]; vis[num[j]] = 1; } if(j<=st)dp[i] = max(dp[i] , dp[j-1]+res);//维护区间最值 } } printf("%d ",dp[n]); }