问题描述
【题目描述】
【输入】
【输出】
【样例输入】
4 3
1 2 3 4
【样例输出】
9
题目解析
暴力法
用三层循环分别枚举K个数相加,用max维护和的最大值。check检查sum%k是否等于0,同时不断更新max的值。
本题可以通过80%的数据,性价比较高。
Hash+拉链法
我们知道三个数相加被k整除可以等价于三个数各个数对k取余后的结果可以被k整除,简单点来说就是
(
a
+
b
+
c
)
%
k
等
价
于
(
a
%
k
+
b
%
k
+
c
%
k
)
%
k
(a+b+c)\%k 等价于(a\%k+b\%k+c\%k)\%k
(a+b+c)%k等价于(a%k+b%k+c%k)%k
也就是说当我们已经知道前两个数a和b时,其实第三个数的范围我们已经知道了,可以利用公式
c
=
(
k
−
a
%
k
+
k
−
b
%
k
)
%
k
c=(k-a\%k+k-b\%k)\%k
c=(k−a%k+k−b%k)%k
也就是说c对k取余后的数是固定的。
则我们可以用二维数组group[k][3]来维护余数为0~k-1的最大的三个数。再用双层循环遍历已经剪枝后的数,可以得到正确答案。
C++代码
#include<bits/stdc++.h>
using namespace std;
int n,K;
void work()
{
cin>>n>>K;
vector<vector<int> > group(K,vector<int>(3)); //相当于int group[1000][3];
for(int i=0;i<n;i++)
{
int x;
cin>>x; //输入数据时就分组
int re = x%K; //余数,用来分组
if(x>group[re][0]) //维护每个分组中最大的三个数
{
group[re][2] = group[re][1];
group[re][1] = group[re][0];
group[re][0] = x;
}
else if(x>group[re][1])
{
group[re][2] = group[re][1];
group[re][1] = x;
}
else if(x>group[re][2])
{
group[re][2] = x;
}
}
long long max = 0,v1,v2,v3;
for(int i=0;i<K;i++) //按照余数枚举第一组
{
for(int j=0;j<K;j++) //按照余数枚举第二组
{
int kk = (K-i+K-j)%K; //得到第三个余数这三个余数凑为k的倍数
v1 = group[i][0];
if(i==j)
{
v2 = group[i][1];
if(i==kk) v3 = group[i][2];
else v3 = group[kk][0];
}
else
{
v2 = group[j][0];
if(i==kk) v3 = group[i][1];
else if(j==kk) v3 = group[j][1];
else v3 = group[kk][0];
}
if(v1+v2+v3>max) max = v1+v2+v3;
}
}
cout<<max<<endl;
}
int main()
{
work();
return 0;
}