牛客小白月赛38

牛客小白月赛38

比赛链接

牛客小白月赛38

C.糟糕的打谱员

题目描述

众所周知,围棋是一项黑白双方轮流行动的棋类游戏。在围棋中,还有打劫的概念,若一方落子在某一个劫争处,则另一方不能立刻也下在这个劫争处,而之后还是可以下的。现在,你是一场史诗级围棋对弈(史诗级体现在这场棋每一步都下在了劫争上)的记谱员,但熬夜到早上七点才睡的你状态不佳,胡乱记了许多东西。

为了假装你有出色的完成记谱工作,请从你记录的谱子中找到一个最长的合法行棋子序列。

一个合法行棋子序列需要满足:

  1. 任意相邻的两步,玩家不同(一黑一白);

  2. 任意相邻的两步,不能下在同一个劫争处。

子序列是指原序列中删除若干(可以不删)元素得到的新序列。

输入描述:

第一行是整数\(T(1\leq T\leq 10)\),测试组数。

每组测试用例,第一行是整数\(n(3\leq n\leq 10^5)\),你记录的谱子长度。

接下来\(n\)行,每行两个数\(c_i(0\leq c_i\leq 1),a_i(1\leq a_i\leq 10)\),表示一步棋,下这步棋的人是\(c_i\)且下在了编号为\(a_i\)的劫争处。

输出描述:

每个测试用例输出一个整数,表示最长的合法行棋子序列长度。

输入

1
20
1 10
0 6
0 9
1 3
0 3
0 5
0 3
1 1
0 2
0 2
0 2
1 9
1 10
1 1
1 10
0 6
0 8
1 1
1 9
0 1

输出

10

解题思路

dp

  • 状态表示:\(f[i]\) 表示以第 \(i\) 个人结束的最长合法子序列长度
  • 状态计算:\(f[i]=max(f[j]+1)\),其中 \(j<i,c[i]!=c[j],a[i]!=a[j]\)
    然而,\(O(n^2)\) 的复杂度会超时!
    \(\color{red}{怎么优化?}\)

可用针对 \(j\) 进行优化,因为 \(c_i\)\(a_i\) 的数据量比较小,我们可用 \(2\times 10\) 的二维数组存下编号,每次我们都取 \(f\) 值较大的编号,这样下次更新时一定是最优的~

  • 时间复杂度:\((20n)\)

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int c[N],a[N],f[N],pos[2][11];
int n;
int main()
{
    int t;
    for(scanf("%d",&t);t;t--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d%d",&c[i],&a[i]);
        memset(f,0,sizeof f);
        int res=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<2;j++)
                if(c[i]!=j)
                    for(int k=1;k<=10;k++)
                        if(a[i]!=k)
                            f[i]=max(f[i],f[pos[j][k]]+1);
            if(f[i]>f[pos[c[i]][a[i]]])pos[c[i]][a[i]]=i;
            res=max(res,f[i]);
        }
        printf("%d\n",res);
    }
    return 0;
}
posted @ 2021-09-19 23:19  zyy2001  阅读(131)  评论(0编辑  收藏  举报