算法刷题整理合集(六)

本篇博客旨在记录自已的算法刷题练习成长,里面注有详细的代码注释以及和个人的思路想法,希望可以给同道之人些许帮助。本人也是算法小白,水平有限,如果文章中有什么错误或遗漏之处,望各位可以在评论区指正出来,各位共勉💪。

文章目录

1、数字接龙

小蓝最近迷上了一款名为《数字接龙》的迷宫游戏,游戏在一个大小为 N×N 的格子棋盘上展开,其中每一个格子处都有着一个 0...K−1 之间的整数。游戏规则如下:

  1. 从左上角
    (0,0) 处出发,目标是到达右下角 (N−1,N−1) 处的格子,每一步可以选择沿着水平/垂直/对角线方向移动到下一个格子。
  2. 对于路径经过的棋盘格子,按照经过的格子顺序,上面的数字组成的序列要满足:0,1,2,...,K−1,0,1,2,...,K−1,0,1,2... 。
  3. 途中需要对棋盘上的每个格子恰好都经过一次(仅一次)。
  4. 路径中不可以出现交叉的线路。例如之前有从 (0,0) 移动到 (1,1) ,那么再从 (1,0) 移动到 (0,1) 线路就会交叉。

为了方便表示,我们对可以行进的所有八个方向进行了数字编号,如下图 2 所示;因此行进路径可以用一个包含 0...7 之间的数字字符串表示,如下图 1 是一个迷宫示例,它所对应的答案就是:41255214。

现在请你帮小蓝规划出一条行进路径并将其输出。如果有多条路径,输出字典序最小的那一个;如果不存在任何一条路径,则输出 −1。

用例规模:

对于 80% 的评测用例:1≤N≤5 。

对于 100% 的评测用例:1≤N≤10,1≤K≤10 。

解题代码:

java 复制代码
import java.util.Scanner;

// dfs
public class Main {
    static int[][] direction = new int[][]{
            {0,-1,0},  // 上
            {1,-1,1}, // 右上
            {1,0,2},  // 右
            {1,1,3},  // 右下
            {0,1,4},  // 下
            {-1,1,5}, // 左下
            {-1,0,6}, // 左
            {-1,-1,7}, // 坐上
    };
    static String[][] hash;
    static int N;
    static int K;
    static int[][] graph;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        N = sc.nextInt();
        K = sc.nextInt();
        graph = new int[N+1][N+1];
        hash = new String[N+1][N+1];
        // 表示棋盘内的数字
        for (int i = 1;i <= N; i++){
            for (int j = 1; j <= N; j++) {
                graph[i][j] = sc.nextInt();
            }
        }
        // 至此准备工作已完成
        if (!dfs(1,1,graph[1][1],"")) System.out.println("-1");

    }

    public static boolean dfs(int x,int y, int k, String str) {
        if (x == N && y == N && str.length() == N * N - 1) {
            System.out.println(str);
            return true;
        }
        for (int[] ints : direction) {
            int X = x + ints[1];
            int Y = y + ints[0];
            //边界判断
            if (X == 0 || Y == 0 || X == N + 1 || Y == N + 1) continue;
            //判断是否连续 且 判断下一个位置是否已被访问过
            if (graph[X][Y] != (k + 1) % K || hash[X][Y] != null) continue;
            // 复合边界条件且下一个位置没被访问过且连续
            if ((X == x || Y == y)||!(check(x, Y, "" + X + y) || check(X, y, "" + x + Y))) {
                // 水平或垂直
                hash[x][y] = "" + X + Y;
                if (dfs(X, Y, (k + 1) % K, str + ints[2])) return true;
                hash[x][y] = null;
            }
        }
        return false;
    }

    public static boolean check(int x, int y, String re) {
        if (hash[x][y] == null) return false;
        else if (hash[x][y].equals(re)) return true;
        return false;
    }
}

2、纪念品

小伟突然获得一种超能力,他知道未来 T 天 N 种纪念品每天的价格。某个纪念品的价格是指购买一个该纪念品所需的金币数量,以及卖出一个该纪念品换回的金币数量。

每天,小伟可以进行以下两种交易无限次:

  1. 任选一个纪念品,若手上有足够金币,以当日价格购买该纪念品;
  2. 卖出持有的任意一个纪念品,以当日价格换回金币。

每天卖出纪念品换回的金币可以立即用于购买纪念品,当日购买的纪念品也可以当日卖出换回金币。当然,一直持有纪念品也是可以的。

T 天之后,小伟的超能力消失。因此他一定会在第 T 天卖出所有纪念品换回金币。

小伟现在有 M 枚金币,他想要在超能力消失后拥有尽可能多的金币。

输入描述:

第一行包含三个正整数 T,N,M,相邻两数之间以一个空格分开,分别代表未来天数 T,纪念品数量 N,小伟现在拥有的金币数量 M。

接下来 T 行,每行包含 N 个正整数,相邻两数之间以一个空格分隔。第 i 行的 N 个正整数分别为 Pi,1,Pi,2,......,Pi,N ,其中 Pi,j 表示第 i 天第 j 种纪念品的价格。

其中,T≤100,N≤100,M≤103,所有价格 1≤Pi,j≤104,数据保证任意时刻,小明手上的金币数不可能超过 104。

输出描述:

输出一行,包含一个正整数,表示小伟在超能力消失后最多能拥有的金币数量。

解题代码:

java 复制代码
import java.util.Arrays;
import java.util.Scanner;

// 动规
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        // 读取未来天数t,纪念品数量n,初始金币数量m
        int t = sc.nextInt();
        int n = sc.nextInt();
        int m = sc.nextInt();

        // 存储每天每种纪念品的价格
        int[][] arr = new int[t+1][n+1];
        for (int i = 1; i <= t; i++) {
            for (int j = 1; j <= n; j++) {
                arr[i][j]=sc.nextInt();
            }
        }
        sc.close();

        // dp[i]表示第 i 天结束时最多拥有的金币数量
        int[] dp = new int[10001];
        int money = m;

        // 遍历每一天
        for (int day = 1; day < t; day++) {
            // 重置dp数组,处理当天的背包问题
            Arrays.fill(dp, 0);
            // 遍历每种纪念品
            for (int item = 1; item <= n; item++) {
                int cost = arr[day][item]; // 当日成本
                int profit = arr[day+1][item] - cost; // 单件利润
                // 完全背包:正序更新; 计算该纪念品当日最多能获得多少利润
                for (int p = cost; p <= money; p++) {
                    dp[p] = Math.max(dp[p], dp[p - cost] + profit);
                }
            }
            // 更新当天结束时拥有的金币数量,以作为第二天的初始金币
            money += dp[money];
        }
        System.out.println(money);
    }
}

3、数的划分

将整数 n 分成 k 份,且每份不能为空,任意两份不能相同(不考虑顺序)。

例如:n=7,k=3,下面三种分法被认为是相同的。

1,1,5;1,5,1;5,1,1;

问有多少种不同的分法。

输入描述:

输入一行,2 个整数 n,k (6≤n≤200,2≤k≤6)。

输出描述:

输出一个整数,即不同的分法。

解题代码:

java 复制代码
import java.util.Scanner;

/**
 * 递归
 */
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n =sc.nextInt();
        int k =sc.nextInt();
        sc.close();
        System.out.println(fun(n,k));
    }

    /**
     * 分两种情况:
     * 一种是至少有一个部分为1。这时候剩下的n-1分成k-1部分。
     * 另一种是所有部分都>=2,这时候每个部分减1,总和减少k,即n-k分成k部分。
     */
    private static int fun(int n, int m) {
        if (n < m) return 0;
        if (n == 1)return 1;
        if (m == 1)return 1;
        return fun(n-m, m)+fun(n-1, m-1);
    }
}

4、选树

已知 n 个整数 x1,x2,⋯,xn,以及一个整数 k(k<n)。从 n 个整数中任选 k 个整数相加,可分别得到一系列的和。例如当 n=4,k=3,4 个整数分别为 3,7,12,19 时,可得全部的组合与它们的和为:

3+7+12=22 3+7+19=29 7+12+19=38 3+12+19=34。

现在,要求你计算出和为素数共有多少种。 例如上例,只有一种的和为素数:3+7+19=29。

输入描述:

n,k(1≤n≤20,k<n)

x1,x2,⋯,xn(1≤xi≤5×106)

输出描述:

一个整数(满足条件的种数)。

解题代码:

java 复制代码
import java.util.Scanner;

public class Main {
    static int[] flat;
    static int ans = 0; // 记录数值相加的和
    static int count = 0; // 素数个数
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int k = sc.nextInt();
        int[] arr = new int[n];
        flat = new int[n];
        for (int i = 0; i < n; i++) {
            arr[i] = sc.nextInt();
        }
        sc.close();

        // 递归排列
        DFS(k,arr,0,0);
        System.out.println(count);
    }

    private static void DFS(int k,int[] nums, int recode, int index){
        // 判断当前递归路径是否已选择了k个数字
        if (recode == k){
            if (prime(ans)){
                count++;
                return;
            }
            return;
        }
        // 遍历整个数组
        for (int i = index; i < nums.length; i++) {
            // 判断当前数字是否被访问过
            if (flat[i] != 1){
                flat[i] = 1; // 标记已访问
                ans += nums[i]; // 将该数字加入
                DFS(k, nums, recode+1, i+1);  // 递归调用
                ans -= nums[i]; // 回溯,撤销数字的加入
                flat[i] = 0; // 回溯,撤销标记
            }
        }
    }

    // 判断是否为素数
    private static boolean prime(int num){
        // 如果num不是素数,那它的一个因数肯定小于等于他的平方根
        for (int i = 2; i <= Math.sqrt(num); i++) {
            if (num % i == 0) return false;
        }
        return true;
    }
}

5、过河卒

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

例如上图 C 点上的马可以控制 9 个点(图中的 P1,P2,⋯P8 和 C)。卒不能通过对方马的控制点。

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

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

解题代码:

java 复制代码
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int bx = sc.nextInt(), by = sc.nextInt(), cx = sc.nextInt(), cy = sc.nextInt();
        int[] targetX = { 0, -1, -2, -2, -1, 1, 2,  2,  1 };
        int[] targetY = { 0, -2, -1,  1,  2, 2, 1, -1, -2 };
        long[][] dp = new long[bx + 1][by + 1];
        for (int i = 0; i < 9; ++i) { //标记马进攻坐标
            int x = cx + targetX[i];
            int y = cy + targetY[i];
            if (x >= 0 && x <= bx && y >= 0 && y <= by) {
                dp[x][y] = -1;
            }
        }
        for (int i = 0; i <= bx; ++i) { // 定义dp坐标x初始值
            if (dp[i][0] != -1) {
                dp[i][0] = 1;
            } else break; //截至前i项
        }
        for (int j = 0; j <= by; ++j) { // 定义dp坐标y初始值
            if (dp[0][j] != -1) {
                dp[0][j] = 1;
            } else break; //截至前j项
        }
        for (int i = 1; i <= bx; ++i) {
            for (int j = 1; j <= by; ++j) {
                if (dp[i][j] == 0) {  //不是马进攻坐标,如果是,则当前的上和左不可转移,作废
//                    dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; //转移
                    dp[i][j] = Math.max(dp[i - 1][j], 0) + Math.max(dp[i][j - 1], 0);
                }
            }
        }
        System.out.print(dp[bx][by]);
    }
}

6、好数

一个整数如果按从低位到高位的顺序,奇数位 (个位、百位、万位 ⋯⋯ ) 上的数字是奇数,偶数位 (十位、千位、十万位 ⋯⋯ ) 上的数字是偶数,我们就称之为 "好数"。

给定一个正整数 NN ,请计算从 1 到 NN 一共有多少个好数。

解题代码:

java 复制代码
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        long n = sc.nextLong();

        int sum = 0;
        for (int i = 1; i <= n; i++) {
            if (pr(i) == 1){
                sum++;
            }
        }
        System.out.println(sum);
    }

    public static int pr(int a) {
        int x = 1;
        while (a != 0){
            int t = a%10;
            // 奇数位上为奇数,偶数位上为偶数
            if (x % 2 == 1){
                if (t % 2 == 0) return 0;
            }else {
                if (t % 2 == 1) return 0;
            }
            x++;
            a /= 10;
        }
        return 1;
    }
}

有帮助的话,希望可以点赞❤️+收藏⭐,谢谢各位大佬~~✨️✨️✨️

相关推荐
董董灿是个攻城狮2 分钟前
Transformer 通关秘籍4:一文看懂文本的 tokenization(分词)的过程
算法
菜鸟学编程o9 分钟前
C++:类和对象(一)
开发语言·c++·算法
kill bert14 分钟前
代码随想录第六十二天| Floyd 算法精讲 A * 算法精讲 (A star算法) 最短路算法总结篇
算法
freeinlife'16 分钟前
贪心算法——思路与例题
算法·贪心算法
农夫阿才33 分钟前
排序算法总结
java·算法·排序算法
Jack电子实验室43 分钟前
STM32实现智能温控系统(暖手宝):PID 算法 + DS18B20+OLED 显示,[学习 PID 优质项目]
stm32·学习·算法
KangkangLoveNLP44 分钟前
从概率到梯度:理解分类问题中交叉熵的优越性
人工智能·深度学习·算法·机器学习·分类·lstm·transformer
围巾哥萧尘1 小时前
「原型设计」Trae AI & DeepSeek-Chat-V3 在微信小程序「倒计时」原型中的复现🧣
算法·ios·架构
高山莫衣1 小时前
【差分隐私相关概念】约束下的矩阵机制
线性代数·算法·矩阵
aimmon1 小时前
Rust从入门到精通之精通篇:24.高级异步编程
开发语言·算法·rust