题目描述
众所周知,小葱同学擅长计算,尤其擅长计算一个数是否是另外一个数的倍数。
但小葱只擅长两个数的情况,当有很多个数之后就会比较苦恼。
现在小葱给了你 n 个数,希望你从这 n 个数中找到三个数
使得这三个数的和是 K 的倍数,且这个和最大。数据保证一定有解。
输入
第一行包括 2 个正整数 n, K。
第二行 n 个正整数,代表给定的 n 个数。
1 <= n <= 10^5, 1 <= K <= 10^3,给定的 n 个数均不超过 10^8。
输出
输出一行一个整数代表所求的和。
样例输入
4 3
1 2 3 4
样例输出
9
思路
1、第一反应暴力,绝对t(我还试了一下,只能过30%)
2、找规律,其实与倍数有关,就只需要关心三个数的余数就够了,只要三个数的余数和能整除k,三个数的和也就能整除k了
3、用一个二维vector可以记录下每个数的余数情况(0-k-1),在对每个余数0-k-1的数进行由大到小排序(每个余数最多只需要用到前三个)
4、只需要用二重循环模拟取数,第三个数由前两个余数可求得,例如前两个余数为i和j,第三个则为k-(i+j)%k,详细见代码
代码
#include<bits/stdc++.h>
#define N 1001
using namespace std;
long long n, k, x;
long long ans = 0;
vector<vector<long long> > a(N);
int cmp(long long x, long long y){
return x>y;
}
int main(){
cin>>n>>k;
for(int i=0; i<n; i++){
cin>>x;
a[x%k].push_back(x);
}
//对0-k-1的数进行从大到小排序
for(int i=0; i<k; i++)
sort(a[i].begin(), a[i].end(), cmp);
//二重循环模拟取数
for(int i=0; i<k; i++){
for(int j=0; j<k; j++){
//第三个余数
int t = k-(i+j)%k;
//如果三个余数都一样
if(t==i && t==j){
//保证该余数存在三个数,否则无法取到这种情况
if(a[i].size()>=3)
ans = max(ans, a[i][0]+a[i][1]+a[i][2]);
}
//如果i和t一样,和j不同,下面以此类推
else if(t==i){
if(a[i].size()>=2 && a[j].size())
ans = max(ans, a[i][0]+a[i][1]+a[j][0]);
}else if(t==j){
if(a[j].size()>=2 && a[i].size())
ans = max(ans, a[j][0]+a[j][1]+a[i][0]);
}else if(i==j){
if(a[i].size()>=2 && a[t].size())
ans = max(ans, a[i][0]+a[i][1]+a[t][0]);
}else{
if(a[i].size() && a[j].size() && a[t].size())
ans = max(ans, a[i][0]+a[j][0]+a[t][0]);
}
}
}
cout<<ans<<endl;
return 0;
}