1. 问题描述:
【题目描述】
众所周知,小葱同学擅长计算,尤其擅长计算一个数是否是另外一个数的倍数。但小葱只擅长两个数的情况,当有很多个 数之后就会比较苦恼。现在小葱给了你 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
2. 思路分析:
① 仔细阅读题目之后发现我们关键是要求解任意选择的三个数字是k的倍数而且和是最大的,第一步想到的是怎么样任意选择三个数字,最基本的思路是可以使用三层循环进行暴力破解,这样就可以任意选择其中的三个数,然后在循环中来判断是否是k的倍数并且可以使用一个临时的变量来决定是否更新当前的最大值,那么循环之后我们就可以知道任意选择的最大值是什么了
② 暴力破解对于数据规模比较小的时候是可以通过的,但是当数据规模大于10 ^ 3之后那么就可能在循环中出不来了,也就是会超时,除了使用暴力破解我们还可以使用深度优先搜索来解决,仔细阅读题目可以发现我们这道题目与部分和的深度优先是类似的,不同的是部分和是若干个数字相加求解出目标值为k的数字的组合,而这道题目求解的是任意的三个数字相加和最大并且是k的倍数,但是两者的本质上是一样的,采用的策略也是一样的
③ 部分和采用的策略是对于当前的数组元素我们可以选择要,也可以选择不要,这样就可以产生所有的数字的组合,这也类似于一棵二叉树,左子树可以要当前这个数组元素,右子树我们可以不要这个元素
④ 根据③的分析我们知道深搜存在着两个平行的状态,一个平行状态是要当前这个元素,另外一个平行状态是不要当前这个元素,所以可以使用两句dfs递归调用来表示
⑤ 确定出口,因为我们是要求解出任意的三个数字,所以需要在递归到数组的最后一个元素,这也就是出口,我们可以使用两个变量来进行记录当前的状态,一个变量是当前已经累加了多少个元素了,另外一个变量是当前累加的数字和是多少,在return之前对这两个变量进行判断是否更新三个值相加并且是k的倍数的值,所以可以从上面的分析可以知道我们只是在部分和的思路上额外增加两个变量传进去dfs方法并且使用if语句来确定是否更新三个数字的最大值
对于四个数字的最大和也是一样的,只是在判断的时候讲变量修改为4即可
总结一下:遇到类似的题目的时候我们最好先是联系之前的模型和相关的思路,可能题目本质上是一样的,可能求解的问题不一样,只需要增加相应的变量来解决自己的需要求解的变量
3. 代码如下:
import java.util.Scanner;
public class Main {
static int max = 0;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int arr[] = new int[n];
int k = sc.nextInt();
for(int i = 0; i <n; i++){
arr[i] = sc.nextInt();
}
dfs(arr, k, 0, 0, 0);
System.out.print(max);
sc.close();
}
private static void dfs(int[] arr, int k, int sum, int cur, int curCount) {
if(sum % k == 0 && curCount == 3 && max < sum){
max = sum;
}
if(cur == arr.length || curCount >= 3) return;
dfs(arr, k, sum + arr[cur], cur + 1, curCount + 1);
dfs(arr, k, sum, cur + 1, curCount);
}
}