【dp】codeforces C. Vladik and Memorable Trip
http://codeforces.com/contest/811/problem/C
【题意】
给定一个自然数序列,在这个序列中找出几个不相交段,使得每个段的异或值之和相加最大。
段的异或值这样定义:段中每个不同数字(不重复)相异或。
段有这样的要求:段中任意一个数字不会在段外出现。
【思路】
首先预处理每个数字第一次出现和最后一次出现的位置,这样对于一个区间[l,r]就很容易判断是否为满足题意的段。
然后区间DP,dp[i]表示子序列[1,i]的最大值。
状态转移:对于dp[i],最小值为dp[i-1],即a[i]在任意段外;如果a[i]的最后一次出现位置大于i,那么没有更新的余地;否则,可能找到这样的l,S.T.dp[i]=max(dp[i],dp[l-1]+sum[l,i])。
如何找到这样的l?
首先可以肯定的是l最小为first[a[i]],然后遍历[first[a[i]],last[a[i]]]中的每个数,不断更新l=min(l,first[a[k]])。
那么为什么dp[i]不可能是dp[j]+sum[j-1,i](j<l)?
因为这样的j一定不满足[j,i]是一个满足题意的段.
如果last[a[j]]<last[a[i]],那么刚刚在遍历[first[a[i]],last[a[i]]]的时候应该已经找到j,即j>=l,矛盾
如果last[a[j]]>last[a[i]],这样的段也不满足题意。
【TLE】
1 #include <iostream> 2 #include <stdio.h> 3 #include <cmath> 4 #include <vector> 5 #include <algorithm> 6 #include <set> 7 #include <map> 8 #include <queue> 9 #include <deque> 10 #include <stack> 11 #include <string> 12 #include <bitset> 13 #include <ctime> 14 #include<algorithm> 15 #include<cstring> 16 using namespace std; 17 int n; 18 const int maxn=5002; 19 int a[maxn]; 20 int fir[maxn]; 21 int last[maxn]; 22 int dp[maxn]; 23 24 int check(int l,int r) 25 { 26 int vis[maxn]; 27 memset(vis,0,sizeof(vis)); 28 for(int i=l;i<=r;i++) 29 { 30 if(last[a[i]]>r||fir[a[i]]<l) 31 { 32 return -1; 33 } 34 } 35 int x=0; 36 for(int i=l;i<=r;i++) 37 { 38 if(!vis[a[i]]) 39 { 40 x^=a[i]; 41 vis[a[i]]=1; 42 } 43 } 44 return x; 45 } 46 int main() 47 { 48 while(~scanf("%d",&n)) 49 { 50 memset(dp,0,sizeof(dp)); 51 memset(fir,0,sizeof(fir)); 52 memset(last,0,sizeof(last)); 53 for(int i=1;i<=n;i++) 54 { 55 scanf("%d",&a[i]); 56 if(!fir[a[i]]) 57 { 58 fir[a[i]]=i; 59 } 60 last[a[i]]=i; 61 } 62 for(int i=1;i<=n;i++) 63 { 64 // cout<<fir[a[i]]<<" "<<last[a[i]]<<endl; 65 } 66 for(int i=1;i<=n;i++) 67 { 68 dp[i]=dp[i-1]; 69 for(int k=1;k<=i;k++) 70 { 71 int num=check(k,i); 72 if(num>0) 73 { 74 dp[i]=max(dp[i],dp[k-1]+num); 75 } 76 } 77 // printf("dp[%d]=%d\n",i,dp[i]); 78 } 79 cout<<dp[n]<<endl; 80 81 } 82 return 0; 83 }
一开始没想到怎样找l的方法,直接枚举,时间复杂度O(n^3)(1^2+2^2+.....+n^2=n(n+1)(2n+1)/6)
【Accepted】
1 #include <iostream> 2 #include <stdio.h> 3 #include <cmath> 4 #include <vector> 5 #include <algorithm> 6 #include <set> 7 #include <map> 8 #include <queue> 9 #include <deque> 10 #include <stack> 11 #include <string> 12 #include <bitset> 13 #include <ctime> 14 #include<algorithm> 15 #include<cstring> 16 using namespace std; 17 int n; 18 const int maxn=5002; 19 int a[maxn]; 20 int fir[maxn]; 21 int last[maxn]; 22 int dp[maxn]; 23 24 int sum(int l,int r) 25 { 26 int vis[maxn]; 27 memset(vis,0,sizeof(vis)); 28 int x=0; 29 for(int i=l;i<=r;i++) 30 { 31 if(!vis[a[i]]) 32 { 33 x^=a[i]; 34 vis[a[i]]=1; 35 } 36 } 37 return x; 38 } 39 int main() 40 { 41 while(~scanf("%d",&n)) 42 { 43 memset(dp,0,sizeof(dp)); 44 memset(fir,0,sizeof(fir)); 45 memset(last,0,sizeof(last)); 46 for(int i=1;i<=n;i++) 47 { 48 scanf("%d",&a[i]); 49 if(!fir[a[i]]) 50 { 51 fir[a[i]]=i; 52 } 53 last[a[i]]=i; 54 } 55 for(int i=1;i<=n;i++) 56 { 57 dp[i]=dp[i-1]; 58 if(last[a[i]]==i) 59 { 60 int l=fir[a[i]]; 61 bool flag=true; 62 for(int k=l+1;k<last[a[i]];k++) 63 { 64 if(last[a[k]]>i) 65 { 66 flag=false; 67 break; 68 } 69 l=min(l,fir[a[k]]); 70 } 71 if(flag) 72 { 73 dp[i]=max(dp[i],dp[l-1]+sum(l,i)); 74 } 75 } 76 } 77 cout<<dp[n]<<endl; 78 } 79 return 0; 80 }