• 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

  • 相关阅读:
    将抓包工具证书从用户目录移动至系统目录,解决反爬对于本地证书认证(安卓7)
    《C++ concurrency in action》 读书笔记 -- Part 2 第三章 线程间的数据共享
    《C++ concurrency in action》 读书笔记 -- Part 3 第四章 线程的同步
    C++14 也快要来了
    《C++ concurrency in action》 读书笔记 -- Part 4 第五章 C++的多线程内存模型 (1)
    利用表达式树构建委托改善反射性能
    使用Task简化Silverlight调用Wcf(再续)
    逆变与协变详解
    Beginning Silverlight 4 in C#数据访问和网络
    使用Task简化Silverlight调用Wcf(续)
  • 原文地址:https://www.cnblogs.com/yuzhangcmu/p/4279676.html
Copyright © 2020-2023  润新知