原题链接:
https://codeforces.com/contest/1497/problem/B
简单来说就是将数列里的数进行分成几组,使每组中相邻的两个数加起来能被m整除,输出最少能分成几组。
我们将每个数取余,并开一个专门用来记录每种余数个数的数组c[]。比如第一个样例的数列:2 2 8 6 9 4,m = 4。
注意到2%4 = 2, 6%4 = 2,而这组数列共有3个数余数为2,依次计数有c[2] = 3。
为了方便我们每次读入一个数就将它的余数记录:
for (int i=1;i<=n;i++){
cin>>a[i];
c[a[i]%m]++;
a[i] = 0;
}
最后将a[]赋值为0。因为之后不再需要用到这个数组,我们只需要专门对c[]进行分类讨论和计数就好。
假设一个数的余数为i,而另一个数余数为m-i,那么它们相加的和一定能被m整除。
如果没有数能和它相加为0,那么一个数为一组。
如果一个数的余数为0,那么他们必定能分在一个组。
同时如果一个数的余数为m/2,那么这一组相邻两个数的和一定也能被m整数。
所以对0进行特判,只要他们各自的余数总计不为0,就一定有一组:
if (c[0]) num++;
(不对m/2进行特判,因为和接下来的分类讨论实质效果是一样的,可以自己模拟一下)
所以我们从余数为1和余数m-1一个一个遍历:
if (c[0]) num++;//余数为0的只要有数 必为一组
for (int i=1;i<m;i++){
//printf("c[%d] = %d\n",i,c[i]);
if (!c[i]&&!c[m-i]) continue;//如果c[i]和c[m-i]都为0,直接开始下一次循环
if (c[i]&&c[m-i]){ //如果都不为0
if (abs(c[i]-c[m-i])<=1)
num++,c[m-i]=0;
else num += abs(c[i]-c[m-i]),c[m-i]=0;
}
else if(c[i]&&!c[m-i) num +=c[i];//如果c[i]不为0,但c[m-i]为0,直接将其加起来
//printf("num = %d\n",num);
}
可能会有读者对中间的if语块判断abs(c[i]-c[m-i])是否小于等于1的操作产生疑问,我们来举个例子。
在第二组样例中: 1 1 1 5 2 4 4 8 6 7 m = 8
通过计数,c[1] = 3, c[7] = 1。
我们要将余数为1和余数为7的数进行组合(题目中正好是1和7)
显然可以注意到,一组放进 1 7 1,而另一组单独放 1,是最少的组数
那么当c[1] = 3,c[7] = 2呢?很高兴的,我们可以这么排列:
1 7 1 7 1
那么只需要一组就好了。
同样的,当c[1] = 3,c[7] = 3,也只需要一组:
1 7 1 7 1 7 (或 7 1 7 1 7 1)
下面两种和第一种有什么区别的?可以观察到,当c[1]和c[7]组合的时候,我们要将数量较小的7放在两个1中间,就能在满足相邻两数相加的余数为0时“节省”组数。
而一个7两边当然只能放两个1,也就是说当abs(c[1]-c[7])>=2 时,所有7两边的位置已经放满了,多余的1只能各自另为一组。
完整代码:
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int t;
const int maxn = 1e5 + 7;
int a[maxn];
int c[maxn];
int main(){
cin>>t;
while (t--){
int n,m;
int num=0;
cin>>n>>m;
if (n==1) cin>>a[1],a[1]=0,cout<<1<<endl;
else{
for (int i=1;i<=n;i++){
cin>>a[i];
c[a[i]%m]++;
a[i] = 0;
}
if (c[0]) num++;//余数为0的只要有数 必为一组
for (int i=1;i<m;i++){
//printf("c[%d] = %d\n",i,c[i]);
if (!c[i]&&!c[m-i]) continue;//如果c[i]和c[m-i]都为0,直接开始下一次循环
if (c[i]&&c[m-i]){ //如果都不为0
if (abs(c[i]-c[m-i])<=1)
num++,c[m-i]=0;
else num += abs(c[i]-c[m-i]),c[m-i]=0;
}
else if(c[i]&&!c[m-i) num +=c[i];//如果c[i]不为0,但c[m-i]为0,直接将其加起来
//printf("num = %d\n",num);
}
for (int i=0;i<=m;i++) c[i] = 0;
cout<<num<<endl;
}
//printf("................\n");
}
}