繁体   English   中英

Java 编程问题,逻辑问题

[英]Java programming question, issue with logic

最近我遇到了一个编程问题,例如,给定一个总数和一个输入 k 找到我们可以从 1 到 k 的数字达到总数的方法总数。

例如:总计 = 5,k = 3

output 应该是 5

因为我们可以使用 1、2 和 3 以 5 种方式达到 5,如下所示

1+1+1+1+1

1+1+1+2

1+2+2

1+1+3

2 + 3

我想出了下面的逻辑,但它并没有完全起作用,因为我没有做回溯(我想),我不知道该怎么做

private static int totalways(int total, int k) {
        List<Integer> arr =  new ArrayList();
        for (int i=0; i<total; i++) {
            arr.add(1);
        }
        int count = 1;
        boolean breakLoop = true;
        while (breakLoop) {
            int last = arr.size()-1;
            for (int i=last; i>=1; i--) {
                if (arr.get(i) + arr.get(i-1) <= k) {
                    count++;
                    int sum = arr.get(i) + arr.get(i-1);
                    arr.remove(i-1);
                    arr.remove(i-1);
                    arr.add(sum);
                }
            }
            if (arr.size() == 2){
                breakLoop = false;
            }
        }
        return count;
    }

任何帮助表示赞赏。

这是一个可以通过动态规划轻松解决的经典问题。 另请参阅此类似问题: https://en.wikipedia.org/wiki/Change-making_problem

第一个观察结果是,当您尝试使用不超过k的数字编写total时,您可以使用k或不使用k

如果您使用k ,那么您仍然必须使用不超过 k 的数字进行total - k k 如果您不使用k ,那么您实际上是在total k-1

如果我们将 c[total][k] 称为total达到k的方法的数量,那么我们的观察给出了一个公式: c[total][k] = c[total-k][k] + c[total][k-1]

编辑:如果k <= total这个公式是真的。 如果k > total ,则c[total][k] = c[total][k-1]

我们还可以观察到,对于k的所有值, c[0][k] = 1 ,对于任何total > 0c[total][0] = 0 0。

编写一个简单的递归程序来使用我们的递归公式将是可怕的; 我们最终会得到指数级的复杂性,因为对于每次调用,我们都需要进行两次递归调用。

相反,我们可以在动态规划算法中使用我们的公式,只需用结果填充二维数组c[][]

int[][] c = new int[total+1][k+1];
for (int n = 1; n <= total; ++n)
{
  c[n][0] = 0;
}
for (int j = 0; j <= k; ++j)
{
  c[0][j] = 1;
}
for (int n = 1; n <= total; ++n)
{
  int maxj = (k <= n) ? k : n;         // maxj = min(k,n)
  for (int j = 1; j <= maxj; ++j)      // case j <= n
  {
    c[n][j] = c[n-j][j] + c[n][j-1];
  }
  for (int j = maxj + 1; j <= k; ++j)  // case j > n
  {
    c[n][j] = c[n][j-1];
  }
}
return c[total][k];

编辑:考虑到案例k > total ,根据评论

以你的例子,total = 5, k = 3,问题是找到一个 function "f(k, total)" 它将尝试从 1 到 k 的所有值 "v" 总和为 "total" 减去“v”。

f(3, 5) then does:
"f" tries 1 and must now sum to 4 i.e. calls f(3, 4)
"f" tries 2 and must now sum to 3 i.e. calls f(3, 3)
"f" tries 3 and must now sum to 2 i.e. calls f(3, 2)

请注意 function f 调用自身。 这称为递归调用。 当您需要回溯时,递归调用通常是简单的解决方案。

这将生成一个调用树,如:

f(3, 5) {}
    f(3, 4) {1}
        f(3, 3) {1, 1}
            f(3, 2) {1, 1, 1}
                f(3, 1) {1, 1, 1, 1}
                    f(3, 0) {1, 1, 1, 1, 1} *
                f(3, 0) {1, 1, 1, 2} *
            f(3, 1) {1, 1, 2}
                f(3, 0) {1, 1, 2, 1} *
            f(3, 0) {1, 1, 3}
...

当“total”参数为 0 时,调用过程停止。

此解决方案生成相同的组合 {1,1,1,2}, {1,1,2,1}...要解决此问题,您可以修改 f 逻辑,以便您永远不会尝试高于“v”的值你的父母来电者尝试了什么。 您的 function 逻辑将是:

f(k, total) {
    ...
    for v in 1 to min(k, total) {
        ...
        f(v, total - v)
    }
} 

新的完整调用树将是:

f(3, 5) {}
    f(1, 4) {1}
        f(1, 3) {1, 1}
            f(1, 2) {1, 1, 1}
                f(1, 1) {1, 1, 1, 1}
                    f(1, 0) {1, 1, 1, 1, 1} *
    f(2, 3) {2}
        f(1, 2) {2, 1}
            f(1, 1) {2, 1, 1}
                f(1, 0) {2, 1, 1, 1} *
        f(2, 1) {2, 2}
            f(1, 0) {2, 2, 1} *
    f(3, 2) {3}
        f(1, 1) {3, 1}
            f(1, 0) {3, 1, 1} *
        f(2, 0) {3, 2} *

您现在要做的就是在总数为 0 时累积找到的解决方案。

为此,您将需要某种类型的堆栈,您将在其中添加当前解决方案。

void f(int k, int total) {
    if (total == 0) {
        System.err.println("add a copy of the current stack to your solutions.");
        return;
    }
    for (int v = 1; v <= Math.min(k, total); ++v) {
        f(v, total - v);
    }
} 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM