【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
",i,dp[i]);
78         }
79         cout<<dp[n]<<endl;
80         
81     }
82     return 0;
83 }
View Code

一开始没想到怎样找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 }
View Code
原文地址:https://www.cnblogs.com/itcsl/p/6934890.html