【编程】【动态规划】

1.数字三角最大值路径

package com.smart.domain;

import java.util.Scanner;
/**
 * http://blog.csdn.net/baidu_28312631/article/details/47418773
 * 递归
 * 递归+缓存(从前往后)
 * 优化空间的DP(从后往前+一维数组)
 * */
/**
 * 重叠子问题---计算最大值 可以等价于第一步+后面最优   后面最优=走第二步+后面最优
 * 状态---边界状态+一般状态
 * 状态转移方程
 * */
/**
 * 能用动规解决的问题的特点
    1.问题具有最优子结构性质。
    如果问题的最优解所包含的子问题的解也是最优的,我们就称该问题具有最优子结构性质。
    2.无后效性。
    当前的若干个状态值一旦确定,则此后过程的演变就只和这若干个状态的值有关,和之前是采取哪种手段或经过哪条路径演变到当前的这若干个状态,没有关系。
 * */
public class Test {
    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        int n=scanner.nextInt();
        int[][] D=new int[n+1][n+1];
        int[] maxarr=new int[n+1];
        for (int i = 1; i <=n ; i++) {
            for (int j = 1; j <=i ; j++) {  //下三角形
                D[i][j]=scanner.nextInt();
            }
        }
            for (int j = 1; j <=n ; j++) {
                maxarr[j]=D[n][j];//maxarr最开始的时候等于最下面数组的元素值
            }
            for (int j = n-1; j >=1 ; j--) {
                for (int k = 1; k <=j ; k++) {
                    maxarr[k]=Math.max(maxarr[k],maxarr[k+1])+D[j][k];
                }
            }
        System.out.println(maxarr[1]);
    }
}

2.最长公共子序列

import java.util.Scanner;

public class Test {
    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        String s1=scanner.next();
        String s2=scanner.next();
        int l1=s1.length();
        int l2=s2.length();
        int l=Math.max(l1,l2);
        int[][]  maxlen=new int[l+1][l+1]; //maxlen[i][j]表示s1(0,i)和s2(0,j)最长公共子序列长度
        //边界状态
        for (int i = 0; i <l ; i++) {
            maxlen[0][i]=0;
            maxlen[i][0]=0;
        }
        //状态转移过程
        for (int i = 1; i <=l1 ; i++) {
            for (int j = 1; j <=l2 ; j++) {
                if (s1.charAt(i-1)==s2.charAt(j-1)){
                    maxlen[i][j]=maxlen[i-1][j-1]+1;
                }else{
                    maxlen[i][j]=Math.max(maxlen[i][j-1],maxlen[i-1][j]);
                }
            }
        }
        System.out.println(maxlen[l1][l2]);
    }
}

3.最长上升子序列

解决方法:

排序+DP

动态

动态+二分搜索


参考资料:http://blog.csdn.net/baidu_28312631/article/details/47426445

解法一:

package com.smart.domain;

/**
 * 解题步骤:
 *
   1. 找子问题
    求序列的前n个元素的最长上升子序列的长度”是个子问题,但这样分解子问题,不具有“无后效性”,因为假设F(n) = x,但可能有多个序列满足F(n) = x。
    有的序列的最后一个元素比 an+1小,则加上an+1就能形成更长上 升子序列;
    有的序列最后一个元素不比an+1小……以后的事情受如何达到状态n的影响,不符合“无后效性” ,
    因此我们必须换一种思路来解决此问题。

    “求以ak(k=1, 2, 3…N)为终点的最长上升子序列的长度”,一个上升子序列中最右边的那个数,称为该子序列的 “终点”。
    虽然这个子问题和原问题形式上并不完全一样,但是只要这N个子问题都解决了,那么这N个子问题的解中, 最大的那个就是整个问题的解。

    2.确定状态
    子问题只和一个变量---数字的位置相关。
    因此序列中数的位置k就是“状态”,而状态 k 对应的“值”,就是以ak做为“终点”的最长上升子序列的长度。
    状态一共有N个。

    3.找出状态转移方程
    maxLen (k)表示以ak做为“终点”的
    最长上升子序列的长度那么:
    初始状态:maxLen (1) = 1
    maxLen (k) = max { maxLen (i):1<=i < k 且 ai < ak且 k≠1 } + 1
    若找不到这样的i,则maxLen(k) = 1
    maxLen(k)的值,就是在ak左边,“终点”数值小于ak ,且长度最大的那个上升子序列的长度再加1。
    因为ak左边任何“终点”小于ak的子序列,加上ak后就能形成一个更长的上升子序列
 * */
import java.util.Scanner;

public class Test {
    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        int n=scanner.nextInt();
        int[] narr=new int[n+1];
        int[] max=new int[n+1];
        for (int i = 1; i <=n ; i++) {
            narr[i]=scanner.nextInt();
            max[i]=1;
        }
        narr[1]=1;
        //人人为我
        for (int i = 2; i <=n ; i++) {
            for (int j = 1; j <i ; j++) {
                if (narr[i]>narr[j]){
                    max[i]=Math.max(max[i],max[j]+1);
                }
            }
        }
        System.out.println(max[n]);
    }
}


解法二:

package com.smart.domain;


import java.util.Scanner;

public class Test {
    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        int n=scanner.nextInt();
        int[] narr=new int[n+1];
        int[] max=new int[n+1];
        for (int i = 1; i <=n ; i++) {
            narr[i]=scanner.nextInt();
            max[i]=1;
        }
        narr[1]=1;
       //我为人人
        for (int i = 1; i <=n ; i++) {
            for (int j = i+1; j <=n ; j++) {
                if (narr[j]>narr[i]){
                    max[j]=Math.max(max[j],max[i]+1);
                }
            }
        }
        System.out.println(max[n]);
    }
}

解法三:

package com.smart.domain;


/**
 * http://blog.csdn.net/shuangde800/article/details/7474903
 * http://www.slyar.com/blog/longest-ordered-subsequence.html
 *
 * 但是需要注意本身栈的弹出还是使用数组
 * 排序+LCS算法 以及 DP算法
 * */
import java.util.Scanner;

public class Test {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int[] narr = new int[n+1];
        int[] mystack=new int[n+1];
        int top=1;//二分查找使用
        for (int i = 1; i <= n; i++) {
            narr[i] = scanner.nextInt();
        }
        mystack[1]=Integer.MIN_VALUE;//先放进一个最小的整数
        for (int i = 1; i < n; i++) {
            if (narr[i]>mystack[top]) {
                mystack[++top]=narr[i];
            }else{
                //二分查找
                int low=0,mid=0,high=top;
                while(low<high){
                    mid=low+(high-low)/2;
                    if (narr[i]>mystack[mid]){
                        low=mid+1;
                    }else if (narr[i]<mystack[mid]){
                        high=mid-1;
                    }
                }
                mystack[low]=narr[i];
            }
        }
        System.out.println(top-1);//插入数据的次数就是最大增长子序列长度  因为更换的操作是不会影响最长长度 但是本身需要减一
    }
}

4.青蛙跳台阶

/**
 * 变态台阶问题
 *  http://www.cnblogs.com/batys/p/3329955.html
 * */
import java.util.Scanner;

public class Test {

    public static int FB(int i){
        if (i==1){
            return 1;
        }else if (i==2){
            return 2;
        }else{
            return FB(i-1)+FB(i-2);
        }
    }
    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        int n=scanner.nextInt();
        int[] narray=new int[n];
        for (int i = 0; i <n ; i++) {
            narray[i]=scanner.nextInt()-1; //注意这道题目已经处在第一个台阶上面了,因此到第二个台阶只有1种方法。  http://exercise.acmcoder.com/online/online_judge_ques?ques_id=1668&konwledgeId=136
        }
        for (int i=0;i<n;i++){
            System.out.println(FB(narray[i]));
        }

    }
}


5.苹果分盘子


package dynamic;

import java.util.Scanner;

/**
 * m个苹果放入n个盘子
 * https://www.nowcoder.com/practice/a2a1d0266629404fba582d416d84b6a0?tpId=61&&tqId=29533&rp=1&ru=/activity/oj&qru=/ta/pku-kaoyan/question-ranking
 * https://my.oschina.net/lucusguo/blog/524678
 * */
public class Test {
    //递归
    public static int division(int m,int n){
        if (m==0||n==1){  //关键点一:理解m==0出现的情况
            return 1;
        }else if (m<n){  //关键点二:m不能等于n
            return division(m,m);
        }else{
            return division(m-n,n)+division(m,n-1);
        }
    }

    //DP
    public static int dp(int m,int n){
        int[][] dparray=new int[m+1][n+1];
        for (int i = 0; i <=m ; i++) {
            for (int j = 1; j <=n ; j++) {
                if (i==0||j==1){
                    dparray[i][j]=1;
                }else{
                    if (i<j){
                        dparray[i][j]=dparray[i][i];
                    }else{
                        dparray[i][j]=dparray[i-j][j]+dparray[i][j-1];
                    }
                }
            }
        }
        return dparray[m][n];
    }
    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        while (scanner.hasNext()){
            int m=scanner.nextInt();//苹果数量
            int n=scanner.nextInt();//盘子数量
            System.out.println(division(m,n));
            System.out.println(dp(m,n));
        }
    }
}

6.最长子串之和

package com.smart.domain;

/**
 *
 * 子问题:
 * 其划分方式和最长增加子序列类似,都不是以后续n-1为整体 而是以ak(k=2,3,...)为终点,考虑前面的结果
 *
 * 状态:
 * s[k]代表数组[0,...k]的和
 *
 * 状态转移方程
 * sum[k]=max{a[k]+sum[k],a[k]}   但是注意这个不是最终的结果  这个只是代表了每个位置最大的和 我们只是需要中间最大值
 * */
/**
 * 如果是子序列?直接大于零的就可以加上
 * 如果是k个子序列?转换为寻找最大最小k个数之和
 * 如果是k个子串?岂不是更加简单 只需要每次计算k个数之和 max就可以了
 * */

//参考链接:http://blog.csdn.net/sgbfblog/article/details/8032464
import java.util.Scanner;

public class Test {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int[] narr = new int[n+1];
        int[] sum=new int[n+1];
        for (int i = 1; i <= n; i++) {
            narr[i] = scanner.nextInt();
        }
        sum[1]=narr[1];
        int max=Integer.MIN_VALUE;
        for (int j = 2; j <=n ; j++) {
           sum[j]=Math.max(sum[j-1]+narr[j],narr[j]);
           if (sum[j]>max){
               max=sum[j];
           }
        }
        System.out.println(max);
    }
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值