文章目录
- 引言
- 正文
- 第一题——下一个字典序的字符
- 个人实现
- 第二题——冒泡排序的变种
- 个人实现
- 空间复杂度比较低的版本
- 第三题——两人走路
- 个人实现
- 总结
引言
- 今天京东笔试做的并不好,有很多问题的关窍都没有找到,所以在很多问题上都浪费了大量的时间!今天看了很多做法,来这里整理一下!
正文
第一题——下一个字典序的字符
个人实现
- 这个题目就是单纯使用对应类似二十六进制的字符进行运算,然后在进行取余进位,但是要防止越界!
import javax.crypto.Cipher;
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
String str = sc.next();
char[] chars = str.toCharArray();
int addNum = 1;
for(int i = chars.length - 1;i >=0 ;i --){
chars[i] = (char)(chars[i] + addNum);
if(chars[i] > 'z' ){
addNum = 1;
chars[i] = 'a' ;
}else{
addNum = 0;
}
}
// 判定是否存在不存在下一个字典序列
if (addNum == 1){
System.out.println(-1);
}else{
System.out.println(new String(chars));
}
}
}
}
注意
- 这个比较特殊是,az的字典序列下一个是ba,不是bz,这里需要重新弄一下
- 整理一下代码,写起来比之前好很多了,不至于那么费劲了!
第二题——冒泡排序的变种
个人实现
- 这里定义了两种操作,一种是正常的冒泡排序,然后交换两个元素,还有一种是三个元素abc,然后交换最外侧的两个元素。具体流程如下
- 题目要求使用最少的操作一去实现,然后给你讲了一个冒泡排序的方式,来误导你!
- 这个时候有两种思维方式
- 方式一:按照题目的提示来,完全按照冒泡排序实现,只不过两个元素改成了三个元素
- 方式二:从整体考虑,纵观一下操作一和操作二的区别
方式一:纯冒泡思考
- 我就是一开始按照题目的诱导实现了纯冒泡排序,然后陷入了一个泥沼,因为对于三个元素,你需要比较一下应该采取那种方式操作更加划算
- 具体应对下述六种情况,每一种情况都需要进行处理,然后计算对应的操作一和操作二的数目,这个还是太浪费时间了,完全没有必要,而且写起来很费劲!我一开始是按照这个写的,但是写到后面发现要处理的情况太多了,整不过来了!
方式二:整体序列考虑
- 这个方式是我在牛客网上看别人的思路分析想出来的,自己当时没想到。
- 操作二仅能操作同为奇数的元素,或者同为偶数的元素,然后对其进行交换顺序,具体见下图
- 连续执行若干次操作二之后,可以发现,整个序列被分成了两部分,一部分是红色元素,还有一部分是蓝色元素,在仅仅执行操作二的情况下,怎么样都不能的交互到一块去!
- 执行若干次操作二的时候,仅仅可以保证两个分开的序列是单调递增的,但是最终的是要求两个序列合并在一块是单调递增的,只能通过操作二,交换对应序列的值才能实现。
- 只能 执行一次操作一之后,在保证在自己的序列里面保证有序就行!
- 借此统计出对应的操作数关系,如果奇数序列和偶数序列都是最终的元素,那么仅仅执行操作二就能有序,如果有不一样的,就需要执行操作一,保证有序。
- 最终又不需要输出有序序列,仅仅是要求执行最少的操作二的次数,具体实现如下
import javax.crypto.Cipher;
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
int n = sc.nextInt();
int[] nums = new int[n];
for (int i = 0; i < n; i++) nums[i] = sc.nextInt();
int[] numsSorted = Arrays.copyOfRange(nums, 0, n);
// 直接对原来的元素进行排序,然后再计算对应的数据就行
Arrays.sort(numsSorted);
Map<Integer,Integer> map1 = new HashMap<>(); // 保存奇数序列的元素
Map<Integer,Integer> map2 = new HashMap<>(); // 保存偶数序列的元素
for (int i = 0; i < n; i++) {
if (i % 2 == 0) {
map2.put(numsSorted[i], map2.getOrDefault(numsSorted[i], 0) + 1);
}else {
map1.put(numsSorted[i], map1.getOrDefault(numsSorted[i], 0) + 1);
}
}
// 计算原始的序列
int oper1 = 0;
for (int i = 0; i < n; i++) {
if (i % 2 == 0) {
if(!map2.containsKey(nums[i])){
oper1 ++;
}
}
}
System.out.println(oper1);
}
}
}
空间复杂度比较低的版本
- 如果不能像上面一样取巧的话,正常应该是先对其进行操作三的排序,然后在对其进行操作二的排序纠正。
- 这里操作一的执行逻辑感觉还是比较复杂的,因为还需要掺杂着执行操作二,想想看,具体问题如下
- 这里是局部有序性的,所以只需要保证结果,所以只需要保证各自序列的最小值是按照序列排序的就行了,后续在再确定一次就行!
import javax.crypto.Cipher;
import java.util.*;
public class Main {
public static void oper2Sort(int[] nums) {
// 使用操作三,类冒泡泡排序
int n = nums.length;
for (int i = n - 1; i >= 0; i--) {
for (int j = 2; j <= i; j++) {
if (nums[j] < nums[j - 2]) {
int temp = nums[j];
nums[j] = nums[j - 2];
nums[j - 2] = temp;
}
}
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
int n = sc.nextInt();
int[] nums = new int[n];
for (int i = 0; i < n; i++) nums[i] = sc.nextInt();
// 使用操作三,类冒泡泡排序
oper2Sort(nums);
System.out.println(Arrays.toString(nums));
int oper1Num = 0;
boolean ordered = false;
while (!ordered) {
ordered = true;
for (int i = 1; i < n; i++) {
if (nums[i] < nums[i - 1]) {
int temp = nums[i];
nums[i] = nums[i - 1];
nums[i - 1] = temp;
oper1Num++;
oper2Sort(nums);
ordered = false;
}
}
}
System.out.println(Arrays.toString(nums));
System.out.println(oper1Num);
}
}
}
总结
- 上述方法存在超时问题,还是得采用取巧的方法,不然我找不到其他合适的方法去计算对应的值了
- 除非还是像之前一样,排个序,看一下当前索引是来自奇数还是偶数,类似两个数组合并,不过还需要创建一个新的数组才行!
第三题——两人走路
- 给一个2行N列的矩阵A,从左上角出发到右下角,然后可以向上、向下、向右移动,不重复访问每一个方格,
- 两个人轮流选一个方格,默认第一个玩家已经选了(1,1),直到路径到达终点的时候,路径停止!
- 路径的定义是所有方格上的数字的综合,第一个玩家想最大路径的成本,第二个玩家想最小化路径的成本,每个人都是选择最优的决策。
- 最后的路径成本将是多少。
个人实现
- 这里dfs的时候比较特殊,需要转换状态进行dfs,不是当前状态再找最优的情况,但是下面这个代码,再牛客上看了很多,发现不能通过所有样例,这里得记录一下状态,但是状态记录比较费劲了,并不知道怎么能够快速匹配到对应的状态!
import java.util.*;
public class Main {
static int N;
static int[][] grid;
static boolean[][] visited;
static int cost = 0;
static boolean big = true;
static boolean small = false;
static int[][] DIRECTIONS = {{0, 1}, {1, 0}, {0, -1}};
public static int[] dfs(int x, int y, boolean status, int cost) {
// 终止条件
if (x == 2 && y == N) {
return new int[]{x, y, cost};
}
int resX = 0;
int resY = 0;
int resCost = status == big ? Integer.MIN_VALUE : Integer.MAX_VALUE;
for (int[] dir : DIRECTIONS) {
int nextX = x + dir[0];
int nextY = y + dir[1];
if (nextX >= 1 && nextX <= 2 && nextY >= 1 && nextY <= N && !visited[nextX][nextY]) {
visited[nextX][nextY] = true;
int[] temp = dfs(nextX, nextY, !status, cost + grid[nextX][nextY]);
if ((status == big && resCost <= temp[2])
|| (status == small && resCost >= temp[2])) {
// 如果相等的情况下,默认是选择能够让对方获得分最小的情况
resCost = temp[2];
resX = nextX;
resY = nextY;
}
// 恢复现场
visited[nextX][nextY] = false;
}
}
return new int[]{resX, resY, resCost};
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
N = sc.nextInt();
grid = new int[3][N + 1];
for (int i = 1; i <= 2; i++) {
for (int j = 1; j <= N; j++) {
grid[i][j] = sc.nextInt();
}
}
// 第一个点已经访问过了
visited = new boolean[3][N + 1];
visited[1][1] = true;
cost = grid[1][1];
// 构建对应的游戏
int curX = 1, curY = 1;
visited[curX][curY] = true;
int[] pos = dfs(curX, curY, small, grid[curX][curY]);
System.out.println(pos[2]);
}
}
}
状态记录
- 这里是使用两个字典来记录各自对应的最优状态,key是对应状态下string字符串来表示,之前都是使用位图来表示的,这个没有办法使用位图表示。但是同一个步骤会有不同的情况,所以要使用map来记录对应的状态记录,然后所见运行空间
- 1表示已经访问过
- 0表示没有访问过
- 2表示当前的点
- value表示下一步的方向
不行,忽然间觉得如果按照我这样的方式进行状态记录,太费时间了,先这样吧!
总结
- 废了九牛二虎之力,终于总结完了笔试,做的不好,还需要再练习!继续加油吧!感觉智力题居多,并没有像其他很多题目一样,会考一些常规的算法思想!