上海有名的效果图公司上海seo推广
本篇博客旨在记录自已的算法刷题练习成长,里面注有详细的代码注释以及和个人的思路想法,希望可以给同道之人些许帮助。本人也是算法小白,水平有限,如果文章中有什么错误或遗漏之处,望各位可以在评论区指正出来,各位共勉💪。
文章目录
- 1、数字接龙
- 2、纪念品
- 3、数的划分
- 4、选树
- 5、过河卒
- 6、好数
1、数字接龙
小蓝最近迷上了一款名为《数字接龙》的迷宫游戏,游戏在一个大小为 N×N 的格子棋盘上展开,其中每一个格子处都有着一个 0…K−1 之间的整数。游戏规则如下:
- 从左上角
(0,0) 处出发,目标是到达右下角 (N−1,N−1) 处的格子,每一步可以选择沿着水平/垂直/对角线方向移动到下一个格子。 - 对于路径经过的棋盘格子,按照经过的格子顺序,上面的数字组成的序列要满足:0,1,2,…,K−1,0,1,2,…,K−1,0,1,2… 。
- 途中需要对棋盘上的每个格子恰好都经过一次(仅一次)。
- 路径中不可以出现交叉的线路。例如之前有从 (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 。
解题代码:
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 种纪念品每天的价格。某个纪念品的价格是指购买一个该纪念品所需的金币数量,以及卖出一个该纪念品换回的金币数量。
每天,小伟可以进行以下两种交易无限次:
- 任选一个纪念品,若手上有足够金币,以当日价格购买该纪念品;
- 卖出持有的任意一个纪念品,以当日价格换回金币。
每天卖出纪念品换回的金币可以立即用于购买纪念品,当日购买的纪念品也可以当日卖出换回金币。当然,一直持有纪念品也是可以的。
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。
输出描述:
输出一行,包含一个正整数,表示小伟在超能力消失后最多能拥有的金币数量。
解题代码:
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,初始金币数量mint 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)。
输出描述:
输出一个整数,即不同的分法。
解题代码:
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)
输出描述:
一个整数(满足条件的种数)。
解题代码:
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 点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。
解题代码:
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 一共有多少个好数。
解题代码:
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;}
}
有帮助的话,希望可以点赞❤️+收藏⭐,谢谢各位大佬~~✨️✨️✨️