牛客小白月赛38 -C 糟糕的打谱员

  • 参考了排名前面的大佬的代码进行了一定的优化

  • 题意描述:给定一个长度为N且有两个元素c,a的序列,其中0<=c<=1,<=a<=10,要求找出一个长度最长的子序列且使得前后两项的c,a两个元素均不相同

  • 根据题意我们很自然的能够想到使用线性dp

  • 我们开始时想到定义状态f[i] [c[i]] [a[i]] 表示在前i项的最长子序列

  • 但我们发现在推导状态转移方程时,前一个状态的i难以判断,需要进行记录

  • 但我们想要记录前面的i无疑需要条件作为数组的下标

  • 联想到了用c和a用来记录

  • 而我们因此可以采用一个pre[c] [a]来记录我们想要的c,a两元素对应最近的位置

  • 上一次c[i]=c,a[i]=a的i序号

  • 而因为pre数组中已经有了关于c,a的信息,我们自然想到在f中可以略去

  • 得到优化后的状态转移方程:

  • ac代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define maxn 300005
    int T,n;
    int c[maxn],a[maxn];
    int f[maxn],pre[maxn][15];
    // 找到一个最长序列,满足前后落子人和落子点不同
    // 状态转移方程
    int main(){
      cin>>T;
      while(T--){
          memset(f,0,sizeof f);
          memset(pre,0,sizeof pre);
          int ans=0;
          cin>>n;
          for(int i=1;i<=n;i++){
              cin>>c[i]>>a[i];
          }
          for(int i=1;i<=n;i++){
              for(int j=1;j<=10;j++){
                  if(a[i]==j)continue;
                  f[i]=max(f[i],f[pre[c[i]^1][j]]+1);
                  pre[c[i]][a[i]]=i;
                  ans=max(ans,f[i]);
              }
    
          }
          cout<<ans<<endl;
      }
    
      return 0;
    }