Description
字节跳动2018校招Android方向(第二批) 作为一个手串艺人,有金主向你订购了一条包含n个杂色串珠的手串——每个串珠要么无色,要么涂了若干种颜色。 为了使手串的色彩看起来不那么单调,金主要求,手串上的任意一种颜色(不包含无色),在任意连续的m个串珠里至多出现一次(注意这里手串是一个环形)。 手串上的颜色一共有c种。现在按顺时针序告诉你n个串珠的手串上,每个串珠用所包含的颜色分别有哪些。 请你判断该手串上有多少种颜色不符合要求。即询问有多少种颜色在任意连续m个串珠中出现了至少两次。
输入格式
第一行输入n,m,c三个数,用空格隔开。(1 <= n <= 10000, 1 <= m <= 1000, 1 <= c <= 50) 接下来n行每行的第一个数num_i(0 <= num_i <= c)表示第i颗珠子有多少种颜色。接下来依次读入num_i个数字,每个数字x表示第i颗柱子上包含第x种颜色(1 <= x <= c)
输出格式
一个非负整数,表示该手链上有多少种颜色不符需求。
输入样例
5 2 3 3 1 2 3 0 2 2 3 1 2 1 3
输出样例
2
提示
第一种颜色出现在第1颗串珠,与规则无冲突。 第二种颜色分别出现在第 1,3,4颗串珠,第3颗与第4颗串珠相邻,所以不合要求。 第三种颜色分别出现在第1,3,5颗串珠,第5颗串珠的下一个是第1颗,所以不合要求。 总计有2种颜色的分布是有问题的。 这里第2颗串珠是透明的。
先上代码
#include<stdio.h>
const int N=1e4+5;
int s[N*2][55];
int n,m,c;
int st[55];
int main()
{
scanf("%d%d%d",&n,&m,&c);
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&s[i][0]);
for(int j=0;j<s[i][0];j++)
{
scanf("%d",&x);
s[i][x]++;
s[i+n][x]++;
}
for(int j=1;j<=c;j++)s[i][j]+=s[i-1][j];
}
for(int i=n+1;i<=2*n;i++)
{
for(int j=1;j<=c;j++)
{
s[i][j]+=s[i-1][j];
}
}
if(m>=n)
{
int num=0;
for(int i=1;i<=c;i++)
{
if(s[n][i]>=2)num++;
}
printf("%d\n",num);
}
else
{
int num=0;
for(int i=m;i<n+m;i++)
{
for(int j=1;j<=c;j++)
{
if(s[i][j]-s[i-m][j]>=2&&!st[j])
{
st[j]=1;
num++;
}
}
}
printf("%d\n",num);
}
return 0;
}
这里先介绍什么是前缀和。
前缀和其实就是高中所学的前n项和,Sn=a1+a2+...+an,那么我们求区间[l,r]中值的和就是前r项和减去前l-1项和(这里涉及到减一,所有建议下标从1开始防止越界)。
那对于这道题,就可以开辟一个二维数组s[i][j]代表前i个串珠中颜色编号为j的出现次数,所以我们要把这个环形手串铺开,变成线性 。
怎么做到像环形一样到末尾又可以到回开始位置?我们可以在数组中再放入相同的n个串珠,这样,我们从下标m(题目要求的连续m个串珠)开始,当到达下标n+m时终止,就可以把所有情况都遍历一遍。
颜色的下标从1开始,那就可以把s[i][0]作为第i个串珠所含的颜色数,后面输入颜色编号为x,就让s[i][x]加1,表明出现过,因为是相同的串珠,所以s[i+n][x]也要加1。
某一个颜色x是否再连续m个珠中出现2次或以上就是求s[i][x]-s[i-m][x]是否大于等于2。
值得注意的是当m>=n时,所有手串都会被选入,这个时候只要看前n个中每个颜色出现次数是否大于等于2就行了,特别判断就行。
最后也是最重要的,不符合标准的颜色在被统计后记得加个标记,即st[i]=1,不然后面可能会再加一次就错了(我就被这个给坑了一下)。