zoukankan      html  css  js  c++  java
  • 【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
  • 相关阅读:
    idea配置tomcat
    使用svn时出现Can't switch /XXX/XXX because it is not the repository yet
    使用idea断点调试时出现no executable code found at line问题
    python 发送邮件
    python中子类调用父类的方法
    Java源码阅读PriorityQueue
    comparable和comparator
    java源码阅读LinkedBlockingQueue
    java源码阅读ArrayBlockingQueue
    java源码阅读LinkedList
  • 原文地址:https://www.cnblogs.com/itcsl/p/6934890.html
Copyright © 2011-2022 走看看