【题目描述】
众所周知,小葱同学擅长计算,尤其擅长计算一个数是否是另外一个数的倍数。但小葱只擅长两个数的情况,当有很多个数之后就会比较苦恼。现在小葱给了你 n 个数,希望你从这 n 个数中找到三个数,使得这三个数的和是 K 的倍数,且这个和最大。数据保证一定有解。
【输入格式】
从标准输入读入数据。
第一行包括 2 个正整数 n, K。
第二行 n 个正整数,代表给定的 n 个数。
【输出格式】
输出到标准输出。
输出一行一个整数代表所求的和。
【样例入】
4 3
1 2 3 4
【样例输出】
9
【样例解释】
选择2、3、4。
【数据约定】
对于 30% 的数据,n <= 100。
对于 60% 的数据,n <= 1000。
对于另外 20% 的数据,K <= 10。
对于 100% 的数据,1 <= n <= 10^5, 1 <= K <= 10^3,给定的 n 个数均不超过 10^8。
解题思路:
暴力O(n^3),只能拿一部分的分
dp:一个状态是合法的,它肯定是由另一个合法状态转化过来的
决策过程和背包问题(n件物品,w[i]和v[i],选择w[i]之和不超过M,且v[i]之和最大)类似,但有些需要注意的点
将v[i]%k作为w[i],(a+b+c)%k==0等价于(a%k+b%k+c%k)%k==0
状态转移方程:
dp[j+1][(t+w[i])%k]=max(dp[j][t]+v[i],dp[j+1][(t+w[i])%k]) 选第i个和不选第i个 (j==0&&t==0)||dp[j][t]
注意:j的遍历从2~0,且放在t循环的外面一层!!
ac代码:
#include <iostream>
#include <cmath>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <stdlib.h>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <sstream>
#define maxn 100005
using namespace std;
int n,k;
int dp[5][1005]={0},w[maxn],v[maxn];
int main()
{
//freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
scanf("%d %d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",&v[i]);
w[i]=v[i]%k;
}
for(int i=1;i<=n;i++)
{
for(int j=2;j>=0;j--)
{
for(int t=k-1;t>=0;t--)
{
if((j==0&&t==0)||dp[j][t])//选了0个和模k为0 or 已经选好了j个和模k为t,更新选j+1个时dp值
dp[j+1][(t+w[i])%k]=max(dp[j][t]+v[i],dp[j+1][(t+w[i])%k]);//选v[i]和不选v[i]
}
}
}
cout<<dp[3][0]<<endl;
return 0;
}
测试样例:
11 3
1 3 6 2 5 7 4 49 21 43 21
输出:99