动态规划!!!动态规划!!!

我一定要把动态规划搞定!!!

0、动态规划必备知识:

http://www.hawstein.com/posts/dp-novice-to-advanced.html

1、最长非降子序列的长度

(这是属于一维动态规划的问题)

题目:一个序列有N个数:A[1],A[2],…,A[N],求出最长非降子序列的长度。

思路:dp[i]表示前i个数中以A[i]结尾的最长非降子序列的长度;想要求dp[i],就把i前面的各个子序列中, 最后一个数不大于A[i]的序列长度加1,然后取出最大的长度即为dp[i]。最有数组dp[]中最大的数,即为最长非降子序列的长度。

代码:

import java.util.Scanner;

public class Solution {

    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        while(sc.hasNext()){
            int n = sc.nextInt();
            int[] A = new int[n];
            for(int i=0; i<n; i++){
                A[i] = sc.nextInt();
            }

            int result = 1;
            //dp[i]表示前i个数中以A[i]结尾的最长非降子序列的长度
            int[] dp = new int[n];
            for(int i=0; i<n; i++){
                dp[i] = 1;
                //把i前面的各个子序列中, 最后一个数不大于A[i]的序列长度加1,然后取出最大的长度即为dp[i]
                for(int j=0; j<i; j++){
                    if(A[j]<A[i]){
                        dp[i] = Math.max(dp[i], dp[j]+1);
                    }
                }

                result = Math.max(result, dp[i]);
            }

            System.out.println(result);
        }

    }

}

2、跳石板

这里写图片描述

思路:https://www.nowcoder.com/questionTerminal/4284c8f466814870bae7799a07d49ec8

代码:

import java.util.Scanner;

public class Solution {

    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        while(sc.hasNext()){
            int N = sc.nextInt();
            int M = sc.nextInt();

            int[] dp = new int[100001];
            for(int i=N; i<=M; i++){
                dp[i] = Integer.MAX_VALUE;      //表示不可到达
            }
            dp[N] = 0;
            for(int i=N; i<M; i++){
                if(dp[i]==Integer.MAX_VALUE){
                    continue;
                }
                //计算约数
                for(int j=2; j*j<=i; j++){
                    if(i%j==0){
                        if((i+j)<=M){
                            dp[i+j] = Math.min(dp[i+j], dp[i]+1);
                        }

                        if((i+i/j)<=M){
                            dp[i+i/j] = Math.min(dp[i+i/j], dp[i]+1);
                        }
                    }
                }

            }

            int result = -1;
            if(dp[M]!=Integer.MAX_VALUE){
                result = dp[M];
            }

            System.out.println(result);

        }

    }

}

3、直方图包含的最大面积

这里写图片描述

这里写图片描述
(参考: http://blog.csdn.net/li563868273/article/details/51121169

代码:

public int countArea(int[] A, int n) {

        int[][] dp = new int[n][n];
        //初始值
        for(int i=0; i<n; i++){
            dp[i][i] = A[i];
        }

        for(int k=1; k<n; k++){
            for(int i=0; (i+k)<n; i++){

                //得到该区间的最小值
                int min = A[i];
                for(int j=i; j<=(i+k); j++){
                    min = ((A[j]<min)?A[j]:min);
                }

                //比较3种情况,得到最大值
                dp[i][i+k] = Math.max(dp[i+1][i+k], dp[i][i+k-1]);
                dp[i][i+k] = Math.max(min*(k+1), dp[i][i+k]);
            }
        }

        return dp[0][n-1];

    }

4、路径问题

这里写图片描述

思路:(参考“必备知识”中的二维DP问题)

代码:

public int countPath(int[][] map, int n, int m) {

        int startX = 0;
        int startY = 0;
        int endX = 0;
        int endY = 0;

        for(int i=0; i<n; i++){
            for(int j=0; j<m; j++){
                if(map[i][j] == 1){
                    startX = j;
                    startY = i;
                }else if(map[i][j] == 2){
                    endX = j;
                    endY = i;
                }
            }
        }
        //确定方向 
        int dirX = (startX<endX ? 1 : -1);
        int dirY = (startY<endY ? 1 : -1);

        int[][] count = new int[n][m];
        count[startY][startX] = 1;

        //确定竖边界的初始值
        for(int i=startY+dirY; i!=(endY+dirY); i+=dirY){
            if(map[i][startX]==-1){
                count[i][startX] = 0;
            }else{
                count[i][startX] = count[i-dirY][startX];  
            }
        }
        //确定横边界的初始值 
        for(int j=startX+dirX; j!=(endX+dirX); j+=dirX){
            if(map[startY][j]==-1){
                count[startY][j] = -1;
            }else{
                count[startY][j] = count[startY][j-dirX];
            }
        }


        for(int i=startY+dirY; i!=(endY+dirY); i+=dirY){
            for(int j=startX+dirX; j!=(endX+dirX); j+=dirX){

                if(map[i][j]==-1){
                    count[i][j] = 0;
                }else{
                    count[i][j] = count[i-dirY][j] + count[i][j-dirX];
                }

            }
        }

        return count[endY][endX];

    }

5、合唱团

这里写图片描述
这里写图片描述

题目来源:网易2017
https://www.nowcoder.com/practice/661c49118ca241909add3a11c96408c8?tpId=85&tqId=29830&tPage=1&rp=1&ru=/ta/2017test&qru=/ta/2017test/question-ranking

解题思路:
http://blog.csdn.net/fcxxzux/article/details/52138964


import java.util.Scanner;

public class Main {

    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        while(sc.hasNext()){
            int n = sc.nextInt();
            int[] a = new int[n+1];
            for(int i=1; i<=n; i++){
                a[i] = sc.nextInt();
            }
            int k = sc.nextInt();
            int d = sc.nextInt();

            //dpMax[i][j]表示以第i个人为最后一个,已经选择了j个人了
            long[][] dpMax = new long[n+1][k+1];
            long[][] dpMin = new long[n+1][k+1];

            long result = 0;
            for(int i=1; i<=n; i++){
                dpMax[i][1] = dpMin[i][1] = a[i];
                for(int j=2; j<=k; j++){

                    if(i<j){
                        dpMax[i][j] = dpMin[i][j] = 0;
                    }else{
                        for(int m=i-1; m>=Math.max(i-d, 1); m--){
                            dpMax[i][j] = Math.max(dpMax[i][j], Math.max(dpMax[m][j-1]*a[i], dpMin[m][j-1]*a[i]));
                            dpMin[i][j] = Math.min(dpMin[i][j], Math.min(dpMax[m][j-1]*a[i], dpMin[m][j-1]*a[i]));
                        }
                    }

                }

                result = Math.max(result, dpMax[i][k]);
            }

            System.out.println(result);
        }
    }


}

6、剪气球

这里写图片描述

题目来源:http://exercise.acmcoder.com/quesexcuse?paperId=213

思路:在计算dp[i+1]时,我们需要考虑第i+1个数可以和前面哪些数分到一起组成连续的子数组。(参考:http://blog.csdn.net/jacky_chenjp/article/details/63684427

代码:

import java.util.Scanner;

public class Main {

    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        while(sc.hasNext()){
            int n = sc.nextInt();
            int[] a = new int[n];
            for(int i=0; i<n; i++){
                a[i] = sc.nextInt();
            }

            int[] dp = new int[n+1];
            dp[0] = 1;
            for(int i=1; i<=n; i++){
                int[] count = new int[10];
                for(int j=i-1; j>=0; j--){
                    count[a[j]]++;
                    if(count[a[j]]>1){
                        break;
                    }else{
                        dp[i] = (dp[i]+dp[j])%1000000007;
                    }
                }

            }

            System.out.println(dp[n]);  
        }

    }

}

7、最长公共子括号序列(来自: 牛客网)

这里写图片描述

  • 29
    点赞
  • 191
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值