一、Problem
Given an array nums of integers, we need to find the maximum possible sum of elements of the array such that it is divisible by three.
Input: nums = [3,6,5,1,8]
Output: 18
Explanation: Pick numbers 3, 6, 1 and 8 their sum is 18 (maximum sum divisible by 3).
Constraints:
1 <= nums.length <= 4 * 10^4
1 <= nums[i] <= 10^4
二、Solution
這題真的想不到 dp,剛開始直接用暴搜做了,但是超時(ps:這題不能用二進制枚舉做,因爲 n 很大,1 << n 是不可能的)
方法一:暴搜
class Solution {
long max; int n, a[];
void dfs(int i, long cur) {
if (i >= n)
return;
if (cur % 3 == 0 && cur > max)
max = cur;
for (int j = i+1; j < n; j++) {
dfs(j, cur+a[j]);
}
dfs(i+1, cur);
}
public int maxSumDivThree(int[] nums) {
a = nums;
n = a.length;
dfs(0, a[0]);
return (int) max;
}
}
複雜度分析
- 時間複雜度:,
- 空間複雜度:,
方法二:dp
這題突破點是要想到:數字總和可劃分爲兩種大的狀態:
- 可被 3 整除;
- 不能被 3 整除,不能被 3 整除又分爲:
- 除以 3 餘 1
- 除以 3 餘 2
所以,dp 的狀態一共有 3 種
- 定義狀態:
- 表示從前 個數選若干個數後,模 3 餘 0 的最大和
- 表示從前 個數選若干個數後,模 3 餘 1 的最大和
- 表示從前 個數選若干個數後,模 3 餘 2 的最大和
- 思考初始化:
- 思考狀態轉移方程:
- 如果 的餘數爲 ,則有
- 思考輸出:
注:這裏要注意的一點就是餘數爲 0 的狀態既可以從餘數爲 0/1/2 三種情況轉移過來,所以這三種都是要枚舉的。
class Solution {
public int maxSumDivThree(int[] a) {
int n = a.length, f[][] = new int[n+1][3];
for (int i = 1; i <= n; i++)
for (int j = 0; j < 3; j++) {
f[i][j] = f[i-1][j];
for (int k = 0; k < 3; k++) { //枚舉f的三種餘數的狀態k,因爲三種餘數狀態都有可能轉移到 j
if ((f[i-1][k] + a[i-1]) % 3 == j)
f[i][j] = Math.max(f[i][j], f[i-1][k] + a[i-1]);
}
}
return f[n][0];
}
}
複雜度分析
- 時間複雜度:,
- 空間複雜度: