题目链接
1234. 倍数问题
众所周知,小葱同学擅长计算,尤其擅长计算一个数是否是另外一个数的倍数。
但小葱只擅长两个数的情况,当有很多个数之后就会比较苦恼。
现在小葱给了你 $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
解题思路
dp,贪心
贪心策略:由于需要找出 $3$ 个和模 $k$ 为 $0$ 的最大数,所以不妨将所有模 $k$ 的余数的所有数提取出来,只需要前 $3$ 就行
这样本质上就是一个背包问题了:
-
状态表示:$f[i][j][t]$ 表示前 $i$ 个数,选了 $t$ 个数,和模 $k$ 结果为 $j$ 的最大和
-
状态计算:$f[i][j][t]=max(f[i-1][j][t],f[i-1][(j-a[i]\%k+k)\%k][t-1])$
这里需要优化空间,滚动掉第一维,但需要逆序循环 $t$,这样是为了防止 $i$ 层更新后的值影响到本身
- 时间复杂度:$O(9000\times k)$
代码
// Problem: 倍数问题
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/1236/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
int n,k,f[1005][4];
int main()
{
cin>>n>>k;
vector<int> a[k];
for(int i=1;i<=n;i++)
{
int x;
cin>>x;
a[x%k].pb(x);
}
memset(f,-0x3f,sizeof f);
f[0][0]=0;
for(int i=0;i<k;i++)
{
sort(a[i].begin(),a[i].end(),greater<>());
for(int j=0;j<3&&j<a[i].size();j++)
{
int x=a[i][j];
for(int t=0;t<k;t++)
for(int u=3;u>=1;u--)f[t][u]=max(f[t][u],f[(t-x%k+k)%k][u-1]+x);
}
}
cout<<f[0][3];
return 0;
}