动态规划例题1(java)

133 阅读6分钟

1.跳台阶

问题描述:

小蓝要上楼梯,楼梯总共n阶,小蓝每一步可以走a,b,c级台阶

请问小蓝总共有多少种方法刚好走到楼梯顶端

public class FrogJump {
    public static int countWays(int n, int a, int b, int c) {
        int[] dp = new int[n+1];
        dp[0] = 1;

        for (int i = 1; i <= n; i++) {
            if (i >= a) {
                dp[i] += dp[i - a];
            }
            if (i >= b) {
                dp[i] += dp[i - b];
            }
            if (i >= c) {
                dp[i] += dp[i - c];
            }
        }

        return dp[n];
    }

    public static void main(String[] args) {
        int n = 10;  // 台阶数
        int a = 1;   // 青蛙可以跳的阶数a
        int b = 2;   // 青蛙可以跳的阶数b
        int c = 3;   // 青蛙可以跳的阶数c

        int result = countWays(n, a, b, c);
        System.out.println("青蛙跳完所有台阶的跳法数量为: " + result);
    }
}

数字三角形

题目描述

观察下面的数字金字塔。

写一个程序来查找从最高点到底部任意处结束的路径,使路径经过数字的和最大。每一步可以走到左下方的点也可以到达右下方的点。

image.png

在上面的样例中,从 7→3→8→7→57→3→8→7→5 的路径产生了最大权值。

输入格式

第一个行一个正整数 �r ,表示行的数目。

后面每行为这个数字金字塔特定行包含的整数。

输出格式

单独的一行,包含那个可能得到的最大的和。

输入输出样例

输入 #1复制

5 7 3 8 8 1 0 2 7 4 4 4 5 2 6 5

输出 #1复制

30

说明/提示

【数据范围】

对于 100%100% 的数据,1≤�≤10001≤r≤1000,所有输入在 [0,100][0,100] 范围内。

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[1001][1001];
        //输入三角形
        for (int i=1;i<=n;i++){
            for (int j=1;j<=i;j++){
                a[i][j] = scanner.nextInt();
            }
        }
        //反向递推
        for (int i=n-1;i>=1;i--){ //从最后一层开始
            for (int j=1;j<=i;j++){
                a[i][j]+=Math.max(a[i+1][j],a[i+1][j+1]);
        }}
        System.out.println(a[1][1]);
    }
}

反向递推,选择比较大的为基底

延伸:将左右移动的差值限定比如往左和往右移的次数差值不能超过1,学完可以去试试

挖地雷

题目描述

在一个地图上有 � (�≤20)N (N≤20) 个地窖,每个地窖中埋有一定数量的地雷。同时,给出地窖之间的连接路径。当地窖及其连接的数据给出之后,某人可以从任一处开始挖地雷,然后可以沿着指出的连接往下挖(仅能选择一条路径),当无连接时挖地雷工作结束。设计一个挖地雷的方案,使某人能挖到最多的地雷。

输入格式

有若干行。

第 11 行只有一个数字,表示地窖的个数 �N

第 22 行有 �N 个数,分别表示每个地窖中的地雷个数。

第 33 行至第 �+1N+1 行表示地窖之间的连接情况:

第 33 行有 �−1n−1 个数(00 或 11),表示第一个地窖至第 22 个、第 33 个 …… 第 �n 个地窖有否路径连接。如第 33 行为 11000⋯011000⋯0,则表示第 11 个地窖至第 22 个地窖有路径,至第 33 个地窖有路径,至第 44 个地窖、第 55 个 …… 第 �n 个地窖没有路径。

第 44 行有 �−2n−2 个数,表示第二个地窖至第 33 个、第 44 个 …… 第 �n 个地窖有否路径连接。

……

第 �+1n+1 行有 11 个数,表示第 �−1n−1 个地窖至第 �n 个地窖有否路径连接。(为 00 表示没有路径,为 11 表示有路径)。

输出格式

第一行表示挖得最多地雷时的挖地雷的顺序,各地窖序号间以一个空格分隔,不得有多余的空格。

第二行只有一个数,表示能挖到的最多地雷数。

输入输出样例

输入 #1复制

5 10 8 4 7 6 1 1 1 0 0 0 0 1 1 1

输出 #1复制

1 3 4 5 27

import java.util.Scanner;
 
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        //表示地窖的个数 n
        int n = sc.nextInt();
        //表示每个地窖中的地窖中的地雷个数
        int[] nums = new int[n + 1],
        //表示到该地窖路径下的最大地雷数
         dp = new int[n + 1];
 
        for (int i = 1; i <= n; i++) {
            nums[i] = dp[i] = sc.nextInt();
        }
 
        //表示地窖间是否有连接
        int[][] arr = new int[n + 1][n + 1];
 
        for (int i = 1; i < n; i++) {
            for (int j = i + 1; j <= n; j++) {
                arr[i][j] = sc.nextInt();
            }
        }
 
        //用来记录路径的数组
        int[] path = new int[n + 1];
        for (int i = n - 1; i >= 1; i--) {
            int maxNum = 0, next = 0;
            for (int j = i + 1; j <= n; j++) {
            //判断i,j是否存在路径
                if (arr[i][j] == 1 && dp[j] > maxNum) {
                    maxNum = dp[j];
                    next = j;
                }
                dp[i] = maxNum + nums[i];
                //将该点存入最大路径中
                path[i] = next;
            }
        }
 
        int index = 1, ans = dp[1];
        for (int i = 1; i <= n; i++) {
            if (dp[i] > dp[index]) {
                ans = dp[i];
                index = i;
            }
        }
		 //遍历路径
        while (index != 0) {
            System.out.print(index + " ");
            index = path[index];
        }
        System.out.println();
 
        System.out.println(ans);
    }
}

过河卒

题目描述

棋盘上 �A 点有一个过河卒,需要走到目标 �B 点。卒行走的规则:可以向下、或者向右。同时在棋盘上 �C 点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。

棋盘用坐标表示,�A 点 (0,0)(0,0)、�B 点 (�,�)(n,m),同样马的位置坐标是需要给出的。

image.png

现在要求你计算出卒从 �A 点能够到达 �B 点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。

输入格式

一行四个正整数,分别表示 �B 点坐标和马的坐标。

输出格式

一个整数,表示所有的路径条数。

输入输出样例

输入 #1复制

6 6 3 3

输出 #1复制

6

说明/提示

对于 100%100% 的数据,1≤�,�≤201≤n,m≤20,0≤0≤ 马的坐标 ≤20≤20。

【题目来源】

NOIP 2002 普及组第四题

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        
        
        //因为数组默认值为0,将坐标整体全部+1,这样边界的就不用去手动复制了
        int n = sc.nextInt()+1, m = sc.nextInt()+1;//B点坐标
        int a = sc.nextInt()+1, b = sc.nextInt()+1;//C点坐标

        int[] x = {0, -2, -1, 1, 2, 2, 1, -1, -2};
        int[] y = {0, 1, 2, 2, 1, -1, -2, -2, -1};
        //判断是否能够走
        boolean[][] road = new boolean[21][21];
        //记录到达该点的路径数
        int  [][] dp = new int [21][21];
        dp[1][1] = 1;
        //记录不能走的
        for (int i = 0; i < x.length; i++) {
            road[a + x[i]][b + y[i]] = true;
        }
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                if (i == 1 && j == 1)
                    continue;
                if (!road[i][j]) {
                    dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
                }

            }

        }

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

    }
}