牛客小白月赛38
牛客小白月赛38
比赛链接
牛客小白月赛38
C.糟糕的打谱员
题目描述
众所周知,围棋是一项黑白双方轮流行动的棋类游戏。在围棋中,还有打劫的概念,若一方落子在某一个劫争处,则另一方不能立刻也下在这个劫争处,而之后还是可以下的。现在,你是一场史诗级围棋对弈(史诗级体现在这场棋每一步都下在了劫争上)的记谱员,但熬夜到早上七点才睡的你状态不佳,胡乱记了许多东西。
为了假装你有出色的完成记谱工作,请从你记录的谱子中找到一个最长的合法行棋子序列。
一个合法行棋子序列需要满足:
-
任意相邻的两步,玩家不同(一黑一白);
-
任意相邻的两步,不能下在同一个劫争处。
子序列是指原序列中删除若干(可以不删)元素得到的新序列。
输入描述:
第一行是整数\(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;
}