簡體   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