1234. 倍数问题
题目链接
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;
}