蓝桥杯 Day 14 java组 DP

        和贪心、分治一样,动态规划是一种解题的思路,而不是一个具体的算法知识点。动态规划是一种需要学习才能获得的思维方法。像贪心、分治这样的方法,在生活中,或在其他学科中有很多类似的例子,很容易联想和理解。但是动态规划不是,它是一种生活中没有的抽象计算方法,没有学过的人很难自发产生这种思路。

 硬币问题

假设有五种面值的 循环五次 

#include<bits/stdc++.h>
using namespace std;
const int M = 251;                   //定义最大金额
const int N = 5;                     //5种硬币
int type[N] = {1, 5, 10, 25, 50};    //5种面值
int cnt[M];                          //每个金额对应最少的硬币数量
const int INF = 0x1FFFFFFF;

void solve(){
    for(int k = 0; k< M; k++)        //初始值为无穷大
       cnt[k] = INF;
    cnt[0] = 0;
    for(int j = 0; j < N; j++)
        for(int i = type[j]; i < M; i++)
            cnt[i] = min(cnt[i], cnt[i - type[j]] + 1);    //递推式
}
int main(){
    int s;
    solve();               //计算出所有金额对应的最少硬币数量。打表
    while(cin >> s){
        if(cnt[s] == INF) cout <<"No answer" << endl;
        else              cout << cnt[s] << endl;
    }
    return 0;
}

 DP(动态规划)的特征

  •  (重叠子问题)首先,子问题是原大问题的小版本,计算步骤完全一样;其次,计算大问题的时候,需要多次重复计算小问题。这就是“重叠子问题”。 一个子问题的多次计算,耗费了大量时间。用 DPDP 处理重叠子问题,每个子问题只需要计算一次,从而避免了重复计算,这就是 DPDP 效率高的原因。具体的做法是:首先分析得到最优子结构,然后用递推或者带记忆化搜索的递归进行编程,从而实现了高效的计算。
  • (最优子结构)

    最优子结构的意思是:首先,大问题的最优解包含小问题的最优解;其次,可以通过小问题的最优解推导出大问题的最优解。在硬币问题中,我们把 cnt[i]的计算,减小为 cnt[i-1] 的计算,也就是把原来为i的大问题,减小为 i-1的小问题,这是硬币问题的最优子结构。

第一题 走楼梯

import java.util.Scanner;

public class Main {
    private static int n;
    private static int[] dp;

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextInt();
        dp = new int[n + 1];
        fun();
    }

    private static void fun() {
        dp[1] = 1;
        dp[2] = 2;
        for (int i = 3; i <= n; i++) {
            dp[i] = dp[i-1] +dp[i-2];
        }
        System.out.println(dp[n]);
        return;
    }
}

其实就是斐波那契数列

同理走楼梯可以扩展成多步走的方式,例如:

import java.util.Scanner;

public class Main {
    private static int n;
    private static int[] dp;

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextInt();
        dp = new int[n + 1];
        fun();
    }

    private static void fun() {
        dp[1] = 1;
        dp[2] = 2;
        dp[3] = 4;
        for (int i = 4; i <= n; i++) {
            dp[i] = dp[i-1] +dp[i-2] +dp[i-3];
        }
        System.out.println(dp[n]);
        return;
    }
}

第二题 最长公共子序列

样例输入

5 6
1 2 3 4 5
2 3 2 1 4 5

样例输出

4

 可以分成两种情况讨论:

import java.util.Scanner;

public class Main {
    private static int a;
    private static int[] a1;
    private static int[] b1;
    private static int b;
    private static int[][] dp;

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        a = scanner.nextInt();
        b = scanner.nextInt();
        dp = new int[a + 1][b + 1];
        a1 = new int[a+1];
        b1 = new int[b+1];
        for (int i = 1; i <= a; i++) {
            a1[i] = scanner.nextInt();
            dp[i][0] = 0;
        }
        for (int i = 1; i <= b; i++) {
            b1[i] = scanner.nextInt();
            dp[0][i] = 0;
        }
        for (int i = 1; i <= a; i++) {
            for (int j = 1; j <= b; j++) {
                if (a1[i] == b1[j]) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }
        System.out.println(dp[a][b]);
    }
}

第三题 最长上升子序列

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int[] a = new int[n];
        int[] dp = new int[n];
        for (int i = 0; i < n; i++) {
            a[i] = scanner.nextInt();
            dp[i] = 0;
        }
        dp[0] = 1;
        for (int i = 1; i < n; i++) {
            int max = 0;
            for (int j = 0; j < i; j++) {
                if (dp[j] > max && a[i] > a[j]) {
                    max = dp[j];
                }
            }
            dp[i] = max +1;
        }
        int max = 0;
        for (int i = 0; i < n; i++) {
            if (dp[i] > max) {
                max = dp[i];
            }
        }
        System.out.println(max);
    }
}

第四题 字符串转换

样例输入

abcf
bcfe

样例输出

2

 把长度 m​​ 的 A​​ 存储在数组 a[1]∼a[m]​​ ,长度为 n​​ 的 B​​ 存储在 b[1]∼b[n]​​,然后定义二维数组 dp​​:dp[i][j]表示 A​ 的前 i​ 个字符转换 B​ 的前 j​ 个字符所需要的操作步骤,那么 dp[m][n]​ 就是答案。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String s1 = scanner.next();
        String s2 = scanner.next();
        char[] c1 = s1.toCharArray();
        char[] c2 = s2.toCharArray();
        int[][] dp = new int[c1.length + 1][c2.length + 1];
        for (int i = 0; i <= c1.length; i++) {
            dp[i][0] = i;
        }
        for (int i = 0; i <= c2.length; i++) {
            dp[0][i] = i;
        }
        for (int i = 1; i <= c1.length; i++) {
            for (int j = 1; j <= c2.length; j++) {
                if (c1[i-1] == c2[j-1]) {
                    dp[i][j] = dp[i - 1][j - 1];
                } else {
                    dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + 1;
                }
            }
        }
        System.out.println(dp[c1.length][c2.length]);
    }
}

第五题 完全背包

 

import java.util.Scanner;

public class Main {
    private static int N;
    private static int V;
    private static int[] v;
    private static int[] w;
    private static int[][] dp;

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        N = scanner.nextInt();
        V = scanner.nextInt();
        v = new int[N + 1];
        w = new int[N + 1];
        dp = new int[N + 1][V + 1];
        for (int i = 1; i <= N; i++) {
            w[i] = scanner.nextInt();//体积
            v[i] = scanner.nextInt();//价值
        }
        for (int i = 1; i <= N; i++) {
            for (int j = 1; j <= V; j++) {
                if (i == 1) {
                    dp[i][j] = 0;
                }
                for (int k = 0; k * w[i] <= j; k++) {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - k * w[i]] + k * v[i]);
                }
            }
        }
        for (int i = 1; i <= N; i++) {
            for (int j = 1; j <= V; j++) {
                System.out.print(dp[i][j] + "  ");
            }
            System.out.println();
        }
        System.out.println(dp[N][V]);
    }
}

第六题 0-1背包简化版

 

样例输入

24
6
8
3
12
7
9
7

样例输出

0

 

import java.util.Scanner;

public class Main {
    private static int V;
    private static int N;
    private static int[] w;
    private static int[] dp;

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        V = scanner.nextInt();
        N = scanner.nextInt();
        w = new int[N + 1];
        dp = new int[V + 1];
        for (int i = 1; i <= N; i++) {
            w[i] = scanner.nextInt();
        }
        for (int i = 1; i <= N; i++) {
            for (int j = V; j >= w[i]; j--) {
                if (dp[j] < dp[j - w[i]] + w[i]) {//在一定范围内使得体积尽可能大,小了就换
                    dp[j] = dp[j - w[i]] + w[i];
                }
            }
        }
        System.out.println(V-dp[V]);
    }
}

 第七题 网格图上的DP

 

 

 

样例输入

6 6 3 3

样例输出

6

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值