查找可被给定数字整除的数组元素的最大总和

时间:2012-11-22 11:33:11

标签: algorithm dynamic-programming

这是一个编程问题。

问题如下:

将给出一组数字以及我们必须除的数字k。 我们必须从该数组中选择元素,使得这些元素的总和可以被k整除。这些元素的总和应该尽可能大。

输入:

在第一行n上,表示元素的数量。

在下一行,给出了数字。

在下一行,我们必须分配k。

输出:

从该数组s.t.中选择元素,尽可能减少总和。 sum可以被k整除。

示例输入:

5 
1 6 2 9 5
8

示例输出:

16

请注意,16可以通过多个数字组合获得,但我们这里只关注最大总和。

我建议的解决方案:

我遍历数组并在给定输入数组的数组b中维护累积和,如:

b=[1 7 9 18 23]

并将数组b中的数字修改为k并将其存储到

c=[1 7 1 2 7]

现在数字在c中具有相同的值,即索引0和索引2;索引1和索引4。 现在我已经得到了所有解决方案,答案就是

max(b[2]-b[0],b[4]-b[1])

在三种情况下,三种指数在c中具有相同的值,即在

的情况下
c=[1 2 3 1 1 2]

答案是

max(b[4]-b[0],b[5]-b[1])

基本上减去最右边出现的那个数字的最左边出现。

我的解决方案仅在存在contiguos元素s.t时才有效。元素之和可被k整除。期待正确解决方案的描述

5 个答案:

答案 0 :(得分:9)

我认为您的解决方案不正确,因为您只考虑连续数字。例如,如果输入是

4
1 6 2 9
8

答案仍然是16(= 1 + 6 + 9)。我不确定你的解决方案是否可以给出这个答案。


要有效解决此问题,请尝试动态编程。我会省略细节,但指出要点。

假设数字位于数组a[i]i1n

f(i,j)表示从a[1]a[i](即a[1], a[2], ..., a[i])中选择数字可以获得的最大金额,并且模k的总和是j

考虑f(i,j),显然我们有两个选择:(1)在总和中包含a[i]; (2)不包括a[i]。因此f(i,j) = max{ f(i-1,x) + a[i], f(i-1,j) }其中x + a[i] == j (mod k)。对于所有f(0,j) = 0

,边界为j

要实现此算法,基本框架如下。

for (j = 0; j < k; j++) f[0][j] = 0;
for (i = 1; i <= n; i++)
  for (j = 0; j < k; j++) {
    x = (j + k - a[i]%k) % k;
    f[i][j] = max(f[i-1][x], f[i-1][j]);
  }

为了节省内存,您还可以使用大小为[2][k]而非[n][k]的数组:

for (j = 0; j < k; j++) f[0][j] = 0;
for (i = 1; i <= n; i++)
  for (j = 0; j < k; j++) {
    x = (j + k - a[i]%k) % k;
    f[i%2][j] = max(f[(i-1)%2][x], f[(i-1)%2][j]);
  }

您还可以使用i&1(和(i-1)&1)来加快2的模数。


关于动态编程的进一步参考:

答案 1 :(得分:3)

听起来像subset sum的变体:您希望具有最大整数的子集可被k整除。

dp[i] = largest sum obtainable that gives remainder i modulo k。但是,为了避免两次使用相同的元素,我们必须使用两个数组,因为模数:包含当前值dpdp1)的数组和包含先前值{的数组{1}}(dp2)。我们有:

dp

(*)请注意,如果执行时间非常重要,则无需进行任何复制。您可以使用数组a = original array dp1[*] = dp2[*] = 0 for i = 1 to n do for j = k - 1 down to 0 do dp1[j] = max(dp1[j], dp2[(j - a[i]) mod k] + a[i]) copy dp1 to dp2: on the next iteration, the current array will must become the previous one (*) 并交替使用其行:计算机从dp[2, k]dp[0, _]进行奇数迭代,反之亦然。甚至迭代。

答案将在dp[1, _]dp1[0, 0]中。使用的内存为dp2[0, 0],时间复杂度为O(n + k)

注意:执行此操作时,您可能需要以这种方式执行模数以避免负值:O(n * k)。或者,如果初始值为负,则可以使用((j - a[i]) mod k + k) mod k并仅添加if

答案 2 :(得分:0)

import java.util.*;

public class MaxSumDivisible 
{

    static int max,divisor;

public static void main(String...okok)
{
    Scanner sc=new Scanner(System.in);
    String str=sc.nextLine();
    String ss[]=str.split(" ");
    LinkedList<Integer> list= new LinkedList<Integer>();
    for(int i=0;i<ss.length;i++)
    {
        list.add(Integer.parseInt(ss[i]));
    }
    divisor=sc.nextInt();
    FindMaxSum(list,0);
    System.out.println(max);
}
public static void FindMaxSum(LinkedList<Integer> list, int currentsum)
{
    if(currentsum%divisor==0 && currentsum>max)
    {
        max=currentsum;
    }

    for(int num:list)
    {
        LinkedList<Integer> li2= new LinkedList<Integer>(list);
        li2.remove(new Integer(num));
        FindMaxSum(li2,currentsum+num);

    }
}
}

它适用于任何数字。(仅适用于int)。

答案 3 :(得分:0)

注意:对于数字为mu3的特殊情况,可以在3时间轻松找到答案。

O(n log n)
现在,如果S = sum(array),那么S % 3 == 0就是答案 如果S,那么要使S % 3 == 1整除的总和,您可以移除3这样的最小元素i,或删除最小的i % 3 == 1,{ {1}} j 如果k,那么您可以移除最小的j % 3 == k % 3 == 2,使S % 3 == 2或最小的ii % 3 == 2移除j。< / p>

答案 4 :(得分:0)

以下代码专门针对给定的数字3。可以被3整除的数组元素的总和。您可以将其进一步推广。 主要思想是为每个mod 3跟踪可以达到的最大和。 时间复杂度:O(N)。 空间复杂度:O(K),其中K是整数,可以将其整除。这里K = 3。

class Solution {
    public int maxSumDivThree(int[] nums) {
        int[] dp = new int[3];
        dp[1] = dp[2] = Integer.MIN_VALUE;
        for(int x : nums) {
            int[] dpNext = new int[3];
            dpNext[0] = Math.max(dp[x%3] + x, dp[0]);
            dpNext[1] = Math.max(dp[(x+1)%3] + x,dp[1]);
            dpNext[2] = Math.max(dp[(x+2)%3] + x,dp[2]);
            dp = dpNext;
        }
        return dp[0];
    }
}

LeetCode每周竞赛163 Link to the problem