2018.12.8——第十二天

题目

典型的动态规划:从金字塔顶端分为左右两个部分,选择左右两种走法中最小的一种,到了最底层只有一个元素,直接返回其本身即可。问题在于java中的这个List好陌生,于是对他进行学习,发现知识量好多,加上今天实验作业怕是无法穷尽,因此先查找学习需要的部分,先加以运用,之后的几天都不轻松,这部分知识慢慢学习吧。

java实现

class Solution {
    public static List<List<Integer>> lp(List<List<Integer>> origin){   //左取
        int n=origin.size();
        List<List<Integer>> array=new ArrayList();
        for(int i=1;i<n;i++)
            array.add(origin.get(i).subList(0,origin.get(i).size()-1));
        return array;
    }
    
    public static List<List<Integer>> rp(List<List<Integer>> origin){   //右取
        int n=origin.size();
        List<List<Integer>> array=new ArrayList();
        for(int i=1;i<n;i++)
            array.add(origin.get(i).subList(1,origin.get(i).size()));
        return array;
    }
    
    public static int minimumTotal(List<List<Integer>> triangle) {
        if (triangle.size()==1)            
            return triangle.get(0).get(0);
        else{
            return Math.min(triangle.get(0).get(0)+minimumTotal(lp(triangle)),triangle.get(0).get(0)+minimumTotal(rp(triangle)));
        }
    }
}

结果自然是时间不够,,动态规划还是复杂度相当高的呀,不过起码复习了动态规划,学习使用了List的操作,其余问题留到以后处理。

在网上的求助以后,发现了有人用很短的代码就可以实现,不禁有点不可置信,但是事实上果真如此

先贴代码和结果

public int minimumTotal(List<List<Integer>> triangle) {
    int n = triangle.size();
    if(n==0){
        return 0;
    }
    int []dp = new int[n];
    for(int i=0;i<n;i++){
        dp[i] = triangle.get(n-1).get(i);
    }

    for(int i=n-2;i>=0;i--){
        for(int j=0;j<=i;j++){
            dp[j] = triangle.get(i).get(j) + Math.min(dp[j],dp[j+1]);
        }
    }
    return dp[0];
}

exm???我代码这么长都没过,这位大神这就能过了?我不信。。

但是看了看代码,发现貌似这思路真的是绝了(没见过世面请见谅),一知半解后去读人家的思路:

动态规划

参考资料

参考分析。http://www.cnblogs.com/themo/p/3688925.html

题图要表达的从顶部到底部的最短路径和,其中路径一词有玄机。路径表示上边节点到下一层的两个分叉。如上例,存在的路径只有:2364,2361,2351,2358,2451,2458,2478,2473

明白题意后,即想到这是一个动态规划问题。思考下转移方程。

f(n,j) = min(f(n-1,j-1),f(n-1,j))+p[i,j]; 貌似有些复杂,我们还得解释一下。

// 转移矩阵
f(n,j) = min(f(n-1 ,j-1), f(n-1 ,j)) + p[n, j];
// f(n, j) 表示从1到n行的,以j为终点的最短路径。
// f(n-1, j) 表示从1到n-1行的,以j为终点的最短路径。
// p[n, j] 表示第n行的第j个元素

对于题目中的例子,我们给出他的状态转移方程构造出来的矩阵

[
     [2],
    [3,4],
   [6,5,7],
  [4,1,8,3]
]

转移矩阵如下
[
         [2],            a. 填入2
       [5, 6],          b. 5 = 2+3,6 = 2+4
    [11,10,13],      c. 11 = 5+6, 10 = 5+min(5,6), 13 = 6+7
  [15,11,18,16]    d. 15 = 11+4, 11 = 1+min(11,10), 18 = 8+min(10,13), 16 = 3+13
]

由上事例可以看出下层的节点的头和尾,因为只有一个直接前驱,所以不需要求min(最小值)这个过程,中间的其余元素都需要比较两个直接前驱中的较小元素。将自身的值和较小元素求和,得到到达自身的最短路径。

题目要求的最短路径,针对上面的问题,等价的解答是,找出状态转移矩阵最底层f(n,j)的最小值。便求得由顶到底的最短路径长度。
--------------------- 
作者:淘气的二进制 
来源:CSDN 
原文:https://blog.csdn.net/fmuma/article/details/80167433 

也是动态规划,甚至人家都列出了状态转移方程,但是根本不需要用递归的函数来实现!

原来我一直思想陷入误区:动归一定要用递归。

其实这道题用数组和循环做同样可以做到动态规划的思想,选择最小的并加到自身上,并把结果作为以后的规划的参考,用数组和循环就够了!

真的是一语惊醒梦中人,收获甚多,相当感谢!

所学知识汇总

Java中对List集合的常用操作

https://www.cnblogs.com/epeter/p/5648026.html

目录:

  1. list中添加,获取,删除元素;
  2. list中是否包含某个元素;
  3. list中根据索引将元素数值改变(替换);
  4. list中查看(判断)元素的索引;
  5. 根据元素索引位置进行的判断;
  6. 利用list中索引位置重新生成一个新的list(截取集合);
  7. 对比两个list中的所有元素;
  8. 判断list是否为空;
  9. 返回Iterator集合对象;
  10. 将集合转换为字符串;
  11. 将集合转换为数组;
  12. 集合类型转换;
  13. 去重复;

 

备注:内容中代码具有关联性。

1.list中添加,获取,删除元素;

  添加方法是:.add(e);  获取方法是:.get(index);  删除方法是:.remove(index); 按照索引删除;  .remove(Object o); 按照元素内容删除;

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

List<String> person=new ArrayList<>();

            person.add("jackie");   //索引为0  //.add(e)

            person.add("peter");    //索引为1

            person.add("annie");    //索引为2

            person.add("martin");   //索引为3

            person.add("marry");    //索引为4

             

            person.remove(3);   //.remove(index)

            person.remove("marry");     //.remove(Object o)

             

            String per="";

            per=person.get(1);

            System.out.println(per);    .get(index)

             

            for (int i = 0; i < person.size(); i++) {

                System.out.println(person.get(i));  //.get(index)

            }

 

2.list中是否包含某个元素;

  方法:.contains(Object o); 返回true或者false

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

List<String> fruits=new ArrayList<>();

            fruits.add("苹果");

            fruits.add("香蕉");

            fruits.add("桃子");

            //for循环遍历list

            for (int i = 0; i < fruits.size(); i++) {

                System.out.println(fruits.get(i));

            }

            String appleString="苹果";

            //true or false

            System.out.println("fruits中是否包含苹果:"+fruits.contains(appleString));

             

            if (fruits.contains(appleString)) {

                System.out.println("我喜欢吃苹果");

            }else {

                System.out.println("我不开心");

            }

 

3.list中根据索引将元素数值改变(替换);

  注意 .set(index, element); 和 .add(index, element); 的不同;

1

2

3

4

5

6

7

8

9

10

11

12

String a="白龙马", b="沙和尚", c="八戒", d="唐僧", e="悟空";

            List<String> people=new ArrayList<>();

            people.add(a);

            people.add(b);

            people.add(c);

            people.set(0, d);   //.set(index, element);     //将d唐僧放到list中索引为0的位置,替换a白龙马

            people.add(1, e);   //.add(index, element);     //将e悟空放到list中索引为1的位置,原来位置的b沙和尚后移一位

             

            //增强for循环遍历list

            for(String str:people){

                System.out.println(str);

            }

 

4.list中查看(判断)元素的索引;  

  注意:.indexOf(); 和  lastIndexOf()的不同;

1

2

3

4

5

6

7

8

9

10

List<String> names=new ArrayList<>();

            names.add("刘备");    //索引为0

            names.add("关羽");    //索引为1

            names.add("张飞");    //索引为2

            names.add("刘备");    //索引为3

            names.add("张飞");    //索引为4

            System.out.println(names.indexOf("刘备"));

            System.out.println(names.lastIndexOf("刘备"));

            System.out.println(names.indexOf("张飞"));

            System.out.println(names.lastIndexOf("张飞"));

 

5.根据元素索引位置进行的判断;

1

2

3

4

5

6

7

if (names.indexOf("刘备")==0) {

    System.out.println("刘备在这里");

}else if (names.lastIndexOf("刘备")==3) {

    System.out.println("刘备在那里");

}else {

    System.out.println("刘备到底在哪里?");

}

 

6.利用list中索引位置重新生成一个新的list(截取集合);

  方法: .subList(fromIndex, toIndex);  .size() ; 该方法得到list中的元素数的和

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

List<String> phone=new ArrayList<>();

            phone.add("三星");    //索引为0

            phone.add("苹果");    //索引为1

            phone.add("锤子");    //索引为2

            phone.add("华为");    //索引为3

            phone.add("小米");    //索引为4

            //原list进行遍历

            for(String pho:phone){

                System.out.println(pho);

            }

            //生成新list

            phone=phone.subList(14);  //.subList(fromIndex, toIndex)      //利用索引1-4的对象重新生成一个list,但是不包含索引为4的元素,4-1=3

            for (int i = 0; i < phone.size(); i++) { // phone.size() 该方法得到list中的元素数的和

                System.out.println("新的list包含的元素是"+phone.get(i));

            }

 

7.对比两个list中的所有元素;

  //两个相等对象的equals方法一定为true, 但两个hashcode相等的对象不一定是相等的对象

1

2

3

4

5

6

7

8

9

10

11

//1.<br>if (person.equals(fruits)) {

    System.out.println("两个list中的所有元素相同");

}else {

    System.out.println("两个list中的所有元素不一样");

}

//2.       

if (person.hashCode()==fruits.hashCode()) {

    System.out.println("我们相同");

}else {

    System.out.println("我们不一样");

}

 

8.判断list是否为空;

  //空则返回true,非空则返回false

1

2

3

4

5

if (person.isEmpty()) {

    System.out.println("空的");

}else {

    System.out.println("不是空的");

}

 

9.返回Iterator集合对象;

1

System.out.println("返回Iterator集合对象:"+person.iterator());

 

1+0.将集合转换为字符串;

1

2

3

String liString="";

liString=person.toString();

System.out.println("将集合转换为字符串:"+liString);

 

11.将集合转换为数组;

1

System.out.println("将集合转换为数组:"+person.toArray());

 

12.集合类型转换;

1

2

3

4

5

6

7

8

9

10

//1.默认类型

List<Object> listsStrings=new ArrayList<>();

  for (int i = 0; i < person.size(); i++) {

    listsStrings.add(person.get(i));

}

//2.指定类型

List<StringBuffer> lst=new ArrayList<>();

  for(String string:person){

  lst.add(StringBuffer(string));

}

 

13.去重复;

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

List<String> lst1=new ArrayList<>();

            lst1.add("aa");

            lst1.add("dd");

            lst1.add("ss");

            lst1.add("aa");

            lst1.add("ss");

 

                   //方法 1.

            for (int i = 0; i <lst1.size()-1; i++) {

                for (int j = lst1.size()-1; j >i; j--) {

                    if (lst1.get(j).equals(lst1.get(i))) {

                        lst1.remove(j);

                    }

                }

            }

            System.out.println(lst1);

             

                   //方法 2.

            List<String> lst2=new ArrayList<>();

            for (String s:lst1) {

                if (Collections.frequency(lst2, s)<1) {

                    lst2.add(s);

                }

            }

            System.out.println(lst2);

List<List<Integer>> arr=new ArrayList();
        List<Integer> s1=new ArrayList();
        List<Integer> s2=new ArrayList();
        List<Integer> s3=new ArrayList();
        List<Integer> s4=new ArrayList();       

        s1.add(2);
        arr.add(s1);
        s2.add(3);s2.add(4);
        arr.add(s2);
        s3.add(6);s3.add(5);s3.add(7);
        arr.add(s3);
        s4.add(4);s4.add(1);s4.add(8);s4.add(3);
        arr.add(s4);

上述代码是想简单构造一个金字塔作为试验,但是一开始是只用了一个s并在每次add一行后s.clear()

但是发现其实最后结果是

4

4 1

4 1 8

4 1 8 3

我发现是这个add类似于引用地址,并不是直接把值传过去,因此每次clear都会使原来的内容一起清除。

多几次试验发现,如果add的是基本类型,则是赋值,但如果是String、数组、集合等等是add的地址,会随原内容一起改变

总结

动态规划不只是递归,只要是有动归思想不管用什么方式都可以实现,我们应该尽量选取实现最简单,开销最小的。

java的集合好多号难呀=。=

这里写图片描述

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值