【算法练习】蓝桥杯训练二:

一、乘积最大(简单、线性DP)

在这里插入图片描述
考虑限制条件:数字串的长度N 和 乘号个数K。用dp[i][j]来表示字符串中前 i 个数,在使用k个乘号的情况下的最大乘积。

那么dp[i][0] = 这个字符串构成的数,我们要找的答案=dp[N - 1][K - 1]。

dp[i][j]怎么得到?我们可以对最后一个乘号:
考虑最后一个乘号,前面还有 j - 1 个乘号,这 j - 1 个乘号,至少需要 j 个数来乘,所以最后一个乘号的枚举位置,应该从 j 开始,枚举到 i - 1的位置,因为最后一个乘号至少需要一位数来乘。

int max = INT_MIN;
// 迭代所有子集
for (int k = j; k <= i - 1; k++) {
	// 字符串长度为 i 个字符,放 j 个乘号的最大乘积
	// k 代表最后一个乘号放的位置,前j - 1个乘号,需要j个字符去乘(至少)
	// 那么最后一个元素可以放在
	max = Math.max(max, dp[k][j - 1] * getCnt(k + 1, i));
}
import java.util.*;

public class Main {
    public static String str = "";
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();
        int k = scan.nextInt();
        str = scan.next();
        // dp[i][j],前 i 个字符放 j 个乘号的最大乘积
        int[][] dp = new int[n + 1][k + 1];
        for (int i = 1; i <= n; i++) {
            // 无乘号的值就是其本身
            dp[i][0] = getCnt(1, i);
        }
        // 前 i 个字符
        for (int i = 1; i <= n; i++) {
            // 放 j 个乘号,注意如果有 i 个字符,最多可以放 i - 1个乘号
            for (int j = 1; j <= i - 1 && j <= k; j++) {
                // 遍历所有子集情况:最后一个乘号放在哪里?
                int max = Integer.MIN_VALUE;
                // 这个kk,枚举的是剩余的j - 1个乘号放在多少个字符之间(至少是j个字符)
                for (int kk = j; kk <= i - 1; kk++) {
                    max = Math.max(max, dp[kk][j - 1] * getCnt(kk + 1, i));
                }
                dp[i][j] = max;
            }
        }
        System.out.println(dp[n][k]);
    }
    // 计算当前区间字符的数字大小
    public static int getCnt(int left, int right) {
        int res = 0;
        for (int i = left - 1; i <= right - 1; i++) {
            res = res * 10 + str.charAt(i) - '0';
        }
        return res;
    }
}

二、方格取数(简单)

在这里插入图片描述
两个人同时走,四维DP(我都Σ(っ °Д °;)っ)
在这里插入图片描述

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();
        int[][] num = new int[n + 1][n + 1];
        // A 1,1 B N,N
        int a, b, c;
        while (true) {
            a = scan.nextInt();b = scan.nextInt();c = scan.nextInt();
            if (a == 0) break;
            num[a][b] = c;
        }
        // 两个人从起点走到终点
        int[][][][] dp = new int[n + 1][n + 1][n + 1][n + 1];
        // dp[i][j][k][l] : A走到i,j,B走到k,l的最大路径和
        // A、B均可向下向右,所以会产生4种情况
        // A下B下,A下B右,A右B下,A右B右
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                for (int k = 1; k <= n; k++) {
                    for (int l = 1; l <= n; l++) {
                        // 取四种情况的最大值
                        dp[i][j][k][l] = Math.max(Math.max(dp[i - 1][j][k - 1][l], dp[i - 1][j][k][l - 1]), Math.max(dp[i][j - 1][k - 1][l], dp[i][j - 1][k][l - 1])) + num[i][j] + num[k][l];
                        if (i == k && j == l) {
                            // 走的是同一格,那就要减回去
                            dp[i][j][k][l] -= num[i][j];
                        }
                    }
                }
            }
        }
        System.out.println(dp[n][n][n][n]);
    }
}

三、求先序排列(简单)

在这里插入图片描述
二叉树…还在磨练中~待更

四、集合运算(简单)

在这里插入图片描述
直接模拟即可

import java.util.*;

public class Main {
    public static void main(String[] args) {

        // 交集
        int[] numA = new int[1000];
        // 并集
        int[] numB = new int[2000];
        // 补集
        int[] numC = new int[1000];

        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();
        int[] A = new int[n];
        HashSet<Integer> setA = new HashSet<>();
        // B在A中的余集中的所有元素:A中剔除所有B中元素
        // 交集、并集、补集下标
        int a = 0, b = 0, c = 0;
        for (int i = 0; i < n; i++) {
            A[i] = scan.nextInt();
            setA.add(A[i]);
            // 并集,A中元素一定都在
            numB[b++] = A[i];
        }
        int m = scan.nextInt();
        int[] B = new int[m];
        HashSet<Integer> setB = new HashSet<>();
        for (int i = 0; i < m; i++) {
            B[i] = scan.nextInt();
            setB.add(B[i]);
            // 交集
            if (setA.contains(B[i])) {
                numA[a++] = B[i];
            }
            // 并集,B中元素一定都在
            numB[b++] = B[i];
        }
        // 求B在A中的余集
        for (int i = 0; i < n; i++) {
            if (setB.contains(A[i])) {
                continue;
            }
            numC[c++] = A[i];
        }
        if (a != 0) {
            Arrays.sort(numA, 0, a);
            for (int i = 0; i < a; i++) {
                System.out.printf("%d ", numA[i]);
            }
            System.out.println();
        }
        Arrays.sort(numB, 0, b);
        for (int i = 0; i < b; i++) {
            if (i > 0 && numB[i] == numB[i - 1]) continue;
            System.out.printf("%d ", numB[i]);
        }
        System.out.println();
        if (c != 0) {
            Arrays.sort(numC, 0, c);
            for (int i = 0; i < c; i++) {
                System.out.printf("%d ", numC[i]);
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@u@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值