[英]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 > 0
的c[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.