lintcode: k Sum 解題報告


13%
Accepted

Given n distinct positive integers, integer k (k <= n) and a number target.

Find k numbers where sum is target. Calculate how many solutions there are?

Example

Given [1,2,3,4], k=2, target=5. There are 2 solutions:

[1,4] and [2,3], return 2.

Tags  Expand 
 

SOLUTION 1:

唉媽,主頁君做這題搞了2,3小時差點吐血。不過,做出來還是很爽嘀!
取自黃老師(九章算法)的思想:
 
 
 
 
 
后面的優化主頁君沒有管。 F[0][0][0]表示在一個空集中找出0個數,target為0,則有1個解,就是什么也不挑嘛!
其實應該這樣寫,也就是說,找0個數,目標為0,則一定是有1個解:

if (j == 0 && t == 0) {
  // select 0 number from i to the target: 0
  D[i][j][t] = 1;
}

1. 狀態表達式:

D[i][j][t] = D[i - 1][j][t];
if (t - A[i - 1] >= 0) {
D[i][j][t] += D[i - 1][j - 1][t - A[i - 1]];
}

意思就是:

(1)我們可以把當前A[i - 1]這個值包括進來,所以需要加上D[i - 1][j - 1][t - A[i - 1]](前提是t - A[i - 1]要大於0)

(2)我們可以不選擇A[i - 1]這個值,這種情況就是D[i - 1][j][t],也就是說直接在前i-1個值里選擇一些值加到target.

代碼:

 1 /**
 2      * @param A: an integer array.
 3      * @param k: a positive integer (k <= length(A))
 4      * @param target: a integer
 5      * @return an integer
 6      */
 7     public int  kSum1(int A[], int k, int target) {
 8         // write your code here
 9         if (target < 0) {
10             return 0;
11         }
12         
13         int len = A.length;
14         
15         int[][][] D = new int[len + 1][k + 1][target + 1];
16         
17         for (int i = 0; i <= len; i++) {
18             for (int j = 0; j <= k; j++) {
19                 for (int t = 0; t <= target; t++) {
20                     if (j == 0 && t == 0) {
21                         // select 0 number from i to the target: 0
22                         D[i][j][t] = 1;
23                     } else if (!(i == 0 || j == 0 || t == 0)) {
24                         D[i][j][t] = D[i - 1][j][t];
25                         if (t - A[i - 1] >= 0) {
26                             D[i][j][t] += D[i - 1][j - 1][t - A[i - 1]];
27                         }
28                     }
29                 }
30             }
31         }
32         
33         return D[len][k][target];
34     }
View Code

 

SOLUTION 2:

我們可以把最外層的Matrix可以省去。

這里最優美的地方,在於我們把target作為外層循環,並且從右往左計算。這里的原因是:

D[i][j][t] += D[i - 1][j - 1][t - A[i - 1]];

這個表達式說明D[i][j][t]是把上一級i的結果累加過來。這里我們省去了i這一級,也就是說在D[j][t]這個表里就地累加。而且t - A[i - 1]小於t。

在以下圖表示就是說D[j][t]是來自於上一行的在t左邊的這些值中挑一些加起來。

所以我們就必須從右往左逐列計算來避免重復的累加。

1. 如果你從左往右按列計算,每一列會被重復地加總,就會有重復計算。我們可以想象一下,len = 0為上表,len = 1為下表。

現在我們只有一個表,就是下面這個(因為第一個維度被取消了),現在如果你從左往右計算,被sum的區域會被填掉,覆蓋

len = 0 那張表留下的值,下一個值的計算就不會准確了。

2. 或者如果你逐行計算,也是不可以的。因為你也是把生成D[j][t](在圖里寫的是D[i][j])的被sum的區域覆蓋,也會造成結果不准確。

3. 所以,只要我們逐列計算,並且順序是從右往左,即使我們只有一個二維表,我們的被sum區域也可以保持潔凈,從空間角度來想,

就相當於從len=0那張表中取值。

總結:這種思維方式可能在面試里很難遇到,不過,可以開拓我們思維,這里同樣是動規時如果取得上一級的值的問題,並且它考慮了省

去一級,就地利用二維空間的值,那么就要考慮我們上一級的舊表不要被覆蓋。可以在大腦中構思一個三維空間,一個三維表由多個二維

表構成,如果把它們用一個表來做,再思考一下即可。

 1 // 2 dimension
 2     public int  kSum(int A[], int k, int target) {
 3         // write your code here
 4         if (target < 0) {
 5             return 0;
 6         }
 7         
 8         int len = A.length;
 9         
10         // D[i][j]: k = i, target j, the solution.
11         int[][] D = new int[k + 1][target + 1];
12         
13         // only one solution for the empty set.
14         D[0][0] = 1;
15         for (int i = 1; i <= len; i++) {
16             for (int t = target; t > 0; t--) {
17                 for (int j = 1; j <= k; j++) {
18                     if (t - A[i - 1] >= 0) {
19                         D[j][t] += D[j - 1][t - A[i - 1]];
20                     }
21                 }
22             }
23         }
24         
25         return D[k][target];
26     }
View Code

 

https://github.com/yuzhangcmu/LeetCode/blob/master/lintcode/dp/KSum.java


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM