计算总和可被 k 整除的子序列总数

Posted

技术标签:

【中文标题】计算总和可被 k 整除的子序列总数【英文标题】:Count total subsequences whose sum is divisible by k 【发布时间】:2015-12-22 23:53:13 【问题描述】:

我正在尝试为该问题编写一个 DP 解决方案:计算元素之和可被 k 整除的数组的可能子序列总数。

我已经编写了以下解决方案。但它没有给出正确的结果。就像下面的代码sn-p,数组是1, 2, 1,k = 3。所以预期能被3整除的子序列总数是2,但实际结果是3,这显然是不正确的。

请指出我的错误。

private int countDP(int[] a, int k)

    int L = a.length;

    int[][] DP = new int[L][k];

    for(int i = 0; i < DP.length; i++)
    
        for(int j = 0; j < DP[0].length; j++)
            DP[i][j] = -1;
    

    int res = _countDP(a, k, DP, 0, 0);

    return res;


private int _countDP(int[] a, int k, int[][] DP, int idx, int m) //Not giving the correct result.

    if(idx == a.length)
        return m == 0 ? 1 : 0;

    if(DP[idx][m] != -1)
        return DP[idx][m];

    int ans = 0;

    ans = _countDP(a, k, DP, idx + 1, m);
    ans += _countDP(a, k, DP, idx + 1, (m + a[idx]) % k);

    return DP[idx][m] = ans;


public static void main(String[] args)

    CountSubnsequences cs = new CountSubnsequences();

    int[] a = 1, 2, 1;
    int k = 3;

    int total1 = cs.countDP(a, k);

    System.out.println("Total numeber of sub sequences: " + total1);

【问题讨论】:

一般两点: 1. 当你写了一个程序,但它不起作用时,我建议你添加打印输出,表明它在做什么:输入了哪个函数,条件状态等。你的就目前而言,问题是“请调试我的代码”。 2.如果您以特定语言在SO中发布代码,您可以考虑将其添加到问题的标签中。 【参考方案1】:

s 表示长度为N 的序列,K 是给定的除数。

dp[i][j] = s[0..i] 的子序列数,余数等于j。我们将为所有0 &lt;= i &lt; N0 &lt;= j &lt; K 计算dp

dp[i][j] = 0 for all  (i, j)

dp[0][0] += 1
dp[0][s[0] mod K] += 1

for i = 1 .. N - 1
    for j = 0 .. K - 1
        dp[i][j] = dp[i - 1][j]
    for j = 0 .. K - 1
        dp[i][(j + s[i]) mod K] += dp[i - 1][j]

结果是dp[N - 1][0]

【讨论】:

算法没有给出正确答案。示例:K = 5. 数组:int[] s = 2, 3, 5, 8;可被 K = 5 整除的子序列为:(2, 3), (2, 8), (5), (2, 5, 8), (2, 3, 5)。所以总共有 5 个子序列的总和可以被 k = 5 整除。但根据你的算法,答案是 6。请查看它。 一个空序列的总和为 0。这就是结果为 6 的原因。 这种情况下结果是否也需要包含空序列? 你问了这个问题。你应该知道的。 逻辑上答案应该是肯定的。但是可以根据上下文进行修改。这只是一个一般性问题。【参考方案2】:
int fun(int i,int s)


    if(i==1)

        if(s-a[i]!=0 && (s-a[i])%k==0)
        return 1;
        else
            return 0;
    else
        if((s-a[i])%k==0)
            return 1+fun(i-1,s-a[i])+fun(i-1,s);
        
        else
            return fun(i-1,s-a[i])+fun(i-1,s);
        
    

【讨论】:

【参考方案3】:

@piotrekg2 解决方案的 Python 代码。 看起来不错!

from typing import List


# dp[i][j] = the number of subsequences of length i with remainder equal to j.
def count_subseq(s: List[int],k):
    n = len(s)
    dp = [0]*k
    dp[0] = 1  # i=0, remainder=0, only 1 subseq
    for i in range(1,n+1):
        dp2 = dp.copy()   # copy previous i-length results: results without s[i] in subseq
        for j in range(k):
            dp2[(j+s[i-1])%k] += dp[j]
        dp = dp2
    return dp[0]


if __name__ == '__main__':
    print(count_subseq([2,3,5,8],5))
    print(count_subseq([5,5,5],5))

【讨论】:

【参考方案4】:

面临同样的问题。但最终得到了答案。

返回的答案总是比可能的子序列总数多 1。这是因为我们知道 0 始终是一个有效的答案。因此,如果假设您没有从数组中选择任何单个元素,那么总和 = 0。因此,它认为它是一个有效的答案,并将我们的答案加 1。因此,要获得实际答案,只需将返回的值减 1。

【讨论】:

以上是关于计算总和可被 k 整除的子序列总数的主要内容,如果未能解决你的问题,请参考以下文章

总和可被k整除的最长子序列[关闭]

给定一个数组,打印所有可能的连续子序列,其总和可被给定数 x 整除

c_cpp 字符串中可被n整除的子序列数

可被 k 整除的子数组数

leetcode 974. 和可被 K 整除的子数组

974. 和可被 K 整除的子数组