目录
大家觉得写得可以的话,可以加入QQ群907575059.

异或和 指初始情况下,所有的铜板堆数量的异或和。
如果有1堆铜板,先手必赢,此时异或和不为0.
如果有2堆铜板,(1)两堆铜板数量相同,先手必输,此时异或和为0(2)两堆铜板数量不同,先手必胜,此时异或和不为0.
如果继续下去,我们可以发现我们最后怎么才能赢,就是拿过之后,必须保证异或和为0。当拿空时,异或和也为0。所以可以得出结论,当初始时异或和不为0,先手赢,但是如果硬币异或和不为0,后手赢。
- /**
- * @ProjectName: study3
- * @FileName: Ex1
- * @author:HWJ
- * @Data: 2023/9/23 16:46
- * 尼姆博弈问题
- */
- public class Ex1 {
- public static void main(String[] args) {
-
- }
-
- public static void getWin(int[] num){
- int xor = num[0];
- for (int i = 1; i < num.length; i++) {
- xor ^= num[i];
- }
- if (xor == 0){
- System.out.println("后手赢!");
- }else {
- System.out.println("先手赢!");
- }
- }
- }

k伪进制和k进制的区别就在于它每个位置上必须有值,值的范围在 [1,k]之间,所以有个普遍想法就是先开始每个位置上尽量放1(从右至左),直到放不了了,然后从那个位置开始往右重新填数。
假如将103用7伪进制表示:1 1 1,只能填三个位置,剩下46,然后用这三个位置继续表示46即可。
- package AdvancedPromotion4;
-
- import java.util.HashMap;
-
- /**
- * @ProjectName: study3
- * @FileName: Ex2
- * @author:HWJ
- * @Data: 2023/9/23 16:56
- */
- public class Ex2 {
- public static void main(String[] args) {
- char[] chars = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
- kConversion(123, chars);
- }
-
- public static void kConversion(int num, char[] chars) {
- int len = chars.length;
- HashMap<Integer, Integer> map = new HashMap<>();
- int max = 0;
- for (int i = 0; Math.pow(len, i) <= num; i++) {
- num -= (int) Math.pow(len, i);
- map.put(i, 1);
- max = i;
- }
- for (int i = max; i >= 0; i--) {
- int a = num / (int) Math.pow(len, i);
- num %= (int) Math.pow(len, i);
- map.put(i, map.get(i) + a);
- }
- StringBuilder str = new StringBuilder();
- for (int i = max; i >= 0; i--) {
- str.append(chars[map.get(i) - 1]);
- }
- System.out.println(str);
- }
-
- }

它不一定要达到最右边才会得到它的最长长度,即它可能在二维数组的任一位置得到最长长度。遍历数组每一个位置得到信息(包括没使用能力的最大长度,和使用能力的最大长度)。
但是每个位置又依靠它的左上位置,,左位置,左下位置,则需要使用递归。可以改为记忆化搜索和动态规划的版本。此题因为不存在枚举行为,所以记忆化搜索和动态规划的时间复杂度和空间复杂度相似。
- package AdvancedPromotion4;
-
- /**
- * @ProjectName: study3
- * @FileName: Ex3
- * @author:HWJ
- * @Data: 2023/9/23 20:12
- */
- public class Ex3 {
- public static void main(String[] args) {
- int[][] matrix = {{1, -4, 10}, {3, -2, -1}, {2, -1, 0}, {0, 5, -2}};
- System.out.println(getMaxLength1(matrix));
- }
-
- public static class Info {
- public int use;
- public int no;
-
- public Info(int no, int use) {
- this.use = use;
- this.no = no;
- }
- }
-
- // 递归版本
- public static int getMaxLength1(int[][] matrix) {
- int res = Integer.MIN_VALUE;
- for (int j = 0; j < matrix[0].length; j++) {
- for (int i = 0; i < matrix.length; i++) {
- Info cur = process1(matrix, i, j);
- int ans = Math.max(cur.no, cur.use);
- res = Math.max(ans, res);
-
- }
- }
- return res;
- }
-
-
- // 递归版本
- public static Info process1(int[][] matrix, int i, int j) {
- if (j == 0) {
- return new Info(matrix[i][j], -matrix[i][j]);
- }
- int preNo = -1;
- int preUse = -1;
- Info preLeft = process1(matrix, i, j - 1);
- if (preLeft.no >= 0) {
- preNo = preLeft.no;
- }
- if (preLeft.use >= 0) {
- preUse = preLeft.use;
- }
-
-
- if (i > 0) {
- Info preUp = process1(matrix, i - 1, j - 1);
- if (preUp.no >= 0) {
- preNo = Math.max(preUp.no, preNo);
- }
- if (preUp.use >= 0) {
- preUse = Math.max(preUse, preUp.use);
- }
- }
-
- if (i < matrix.length - 1) {
- Info preDown = process1(matrix, i + 1, j - 1);
- if (preDown.no >= 0) {
- preNo = Math.max(preDown.no, preNo);
- }
- if (preDown.use >= 0) {
- preUse = Math.max(preUse, preDown.use);
- }
- }
- int use = -1;
- int no = -1;
- if (preUse >= 0) {
- use = preUse + matrix[i][j];
- }
- if (preNo >= 0) {
- no = preNo + matrix[i][j];
- use = Math.max(use, preNo - matrix[i][j]);
- }
- return new Info(no, use);
- }
-
- // 记忆化搜索版本
- public static int getMaxLength2(int[][] matrix) {
- int res = Integer.MIN_VALUE;
- Info[][] map = new Info[matrix.length][matrix[0].length];
- for (int j = 0; j < matrix[0].length; j++) {
- for (int i = 0; i < matrix.length; i++) {
- Info cur = process2(matrix, i, j, map);
- int ans = Math.max(cur.no, cur.use);
- res = Math.max(ans, res);
-
- }
- }
- return res;
- }
-
- // 记忆化搜索版本版本
- public static Info process2(int[][] matrix, int i, int j, Info[][] map) {
- if (map[i][j] != null) { // 如果已经得到信息的地方,之间调用之前的信息
- return map[i][j];
- }
- if (j == 0) {
- map[i][j] = new Info(matrix[i][j], -matrix[i][j]);
- return map[i][j];
- }
- int preNo = -1;
- int preUse = -1;
- Info preLeft = process2(matrix, i, j - 1, map);
- if (preLeft.no >= 0) {
- preNo = preLeft.no;
- }
- if (preLeft.use >= 0) {
- preUse = preLeft.use;
- }
-
-
- if (i > 0) {
- Info preUp = process2(matrix, i - 1, j - 1, map);
- if (preUp.no >= 0) {
- preNo = Math.max(preUp.no, preNo);
- }
- if (preUp.use >= 0) {
- preUse = Math.max(preUse, preUp.use);
- }
- }
-
- if (i < matrix.length - 1) {
- Info preDown = process2(matrix, i + 1, j - 1, map);
- if (preDown.no >= 0) {
- preNo = Math.max(preDown.no, preNo);
- }
- if (preDown.use >= 0) {
- preUse = Math.max(preUse, preDown.use);
- }
- }
- int use = -1;
- int no = -1;
- if (preUse >= 0) {
- use = preUse + matrix[i][j];
- }
- if (preNo >= 0) {
- no = preNo + matrix[i][j];
- use = Math.max(use, preNo - matrix[i][j]);
- }
- map[i][j] = new Info(no, use);
- return map[i][j];
- }
- }

先用栈实现不含括号的表达式求值,如果遇到运算符,且此时栈顶的运算符不为乘,除,就将运算符之前的数值和运算符加入栈,否则弹出运算符和运算符之前的数值,并且将其和当前数值进行运算后,在再将运算的值和当前运算符加入栈。
实现这个后我们考虑一个信息结构process(str,i)返回两个数值a,b a表示从i开始到停止地方得到的运算结果,b表示它在哪里停止。遇到右括号或者到边界就停止。
- package AdvancedPromotion4;
-
- import java.util.LinkedList;
-
- /**
- * @ProjectName: study3
- * @FileName: Ex4
- * @author:HWJ
- * @Data: 2023/9/23 20:49
- */
- public class Ex4 {
- public static void main(String[] args) {
- String str = "48*((70-65)-43)+8*1";
- System.out.println(getNum(str));
- }
-
- public static int getNum(String str) {
- return process(str, 0)[0];
- }
-
-
- // 将每一个括号里面包含的运算式记作为一个整体进行计算。
- public static int[] process(String str, int index) {
- LinkedList<String> list = new LinkedList<>();
- int num = 0;
- while (index < str.length() && str.charAt(index) != ')') {
- if (str.charAt(index) >= '0' && str.charAt(index) <= '9') {
- num = num * 10 + str.charAt(index) - '0';
- index++;
- } else if (str.charAt(index) == '(') { // 这里遇到左括号
- int[] ans = process(str, index + 1);
- index = ans[1] + 1; // 此次运算的结束位置
- num = ans[0]; // 括号里运算式总体的运算结果。
- } else { // 遇到运算符
- addNum(list, num);
- list.addLast(String.valueOf(str.charAt(index++)));
- num = 0;
- }
- }
- addNum(list, num);
- return new int[]{totalNum(list), index};
- }
-
-
- // 当栈顶运算符为乘或除时,暂时对运算式进行计算。
- public static void addNum(LinkedList<String> list, int num) {
- if (!list.isEmpty()) {
- int cur = 0;
- String operator = list.pollLast();
- if (operator.equals("+") || operator.equals("-")) {
- list.addLast(operator);
- } else {
- cur = Integer.valueOf(list.pollLast());
- num = operator.equals("*") ? num * cur : cur / num;
-
- }
- }
- list.addLast(String.valueOf(num));
- }
-
-
- // 处理完后整个栈里只剩下数值和加减运算符
- public static int totalNum(LinkedList<String> list) {
- int res = 0;
- boolean add = true;
- String cur = null;
- int num = 0;
- while (!list.isEmpty()) {
- cur = list.pollFirst();
- if (cur.equals("+")) {
- add = true;
- }else if(cur.equals("-")){
- add = false;
- }else {
- num = Integer.valueOf(cur);
- res += add ? num : (-num);
- }
- }
- return res;
- }
- }

因为子串一定是在字符串中连续的字符子串。两个字符串的最长公共子串可能以str1的任意位置结尾和str2的任意位置结尾,但是因为是公共子串则要求两个的对应位置字符相同,所以如果str1以i结尾和str2以j结尾的公共子串长度取决于str1以i - 1结尾和str2以j - 1结尾的公共子串长度 + 1.(str1以i结尾和str2以j结尾的字符相同)
根据标红的对应关系可知,dp[i][j]只依赖与dp[i-1][j-1],所以我们可以不必要创建一个二维数组来进行dp。例如如下规则来对动态规划空间进行压缩。
| 10 | 7 | 4 | 2 | 1 |
| 13 | 11 | 8 | 5 | 3 |
| 15 | 14 | 12 | 9 | 6 |
以上数字代表优化后的填表技巧,当填3时,我们只记录2,填4时只记录3.将整个表优化为一个变量。
- package AdvancedPromotion4;
-
-
- /**
- * @ProjectName: study3
- * @FileName: Ex5
- * @author:HWJ
- * @Data: 2023/9/23 21:27
- */
- public class Ex5 {
- public static void main(String[] args) {
- String str1 = "kaaabcFght";
- String str2 = "ksaaabmFght";
- System.out.println(getSameLen1(str1, str2));
- System.out.println(getSameLen2(str1, str2));
- }
-
- public static int getSameLen1(String s1, String s2){
- int[][] dp = new int[s1.length()][s2.length()];
- char[] str1 = s1.toCharArray();
- char[] str2 = s2.toCharArray();
- int res = 0;
- for (int i = 0; i < str1.length; i++) {
- for (int j = 0; j < str2.length; j++) {
- if (i == 0 || j == 0){
- if (str1[i] == str2[j]){
- dp[i][j] = 1;
- res = Math.max(dp[i][j], res);
- }
- }else {
- if (str1[i] == str2[j]){
- dp[i][j] = dp[i - 1][j - 1] + 1;
- res = Math.max(dp[i][j], res);
- }
- }
- }
- }
- return res;
- }
-
- public static String getSameLen2(String s1, String s2){
- int len = 0;
- char[] str1 = s1.toCharArray();
- char[] str2 = s2.toCharArray();
- int max = 0;
- int end = 0;
- int row = 0;
- int col = s2.length() - 1;
- while (row < s1.length()){
- len = 0;
- int i = row;
- int j = col;
- while (i < s1.length() && j < s2.length()){
- if (str1[i] == str2[j]){
- len++;
- if (len > max){
- max = len;
- end = i;
- }
- }else {
- len = 0;
- }
- i++;
- j++;
- }
-
- if (col > 0){
- col--;
- }else {
- row++;
- }
- }
- return s1.substring(end - max + 1, end + 1);
- }
- }

因为子串一定是在字符串中满足原顺序的字符序列,可以不连续。
所以对任意dp[i][j]表示以i位置结尾的str1,表示以j位置结尾的str2的两个子串上最长公共子序列的长度。
对于这个最长公共子序列有4种可能性:
(1)最长公共子序列包含i位置,包含j位置。
(2)最长公共子序列不包含i位置,包含j位置。
(3)最长公共子序列包含i位置,不包含j位置。
(4)最长公共子序列不包含i位置,不包含j位置。
- package AdvancedPromotion4;
-
- /**
- * @ProjectName: study3
- * @FileName: Ex6
- * @author:HWJ
- * @Data: 2023/9/23 22:04
- */
- public class Ex6 {
- public static void main(String[] args) {
- String str1 = "kaaabcFght";
- String str2 = "ksaaabmFght";
- System.out.println(getSameLen(str1, str2));
- }
-
- public static int getSameLen(String s1, String s2){
- int[][] dp = new int[s1.length()][s2.length()];
- char[] str1 = s1.toCharArray();
- char[] str2 = s2.toCharArray();
- int res = 0;
- for (int i = 0; i < str1.length; i++) {
- for (int j = 0; j < str2.length; j++) {
- if (i == 0 && j == 0){
- if (str1[i] == str2[j]){
- dp[i][j] = 1;
- res = dp[i][j];
- }
- }else {
- if (i == 0){
- dp[i][j] = dp[i][j - 1];
- res = Math.max(dp[i][j], res);
- }else if(j == 0){
- dp[i][j] = dp[i - 1][j];
- res = Math.max(dp[i][j], res);
- }else {
- int p1 = dp[i][j - 1];
- int p2 = dp[i - 1][j];
- int p3 = dp[i - 1][j - 1] + (str1[i] == str2[j] ? 1 : 0);
- dp[i][j] = Math.max(p1, Math.max(p2, p3));
- res = Math.max(dp[i][j], res);
- }
-
- }
- }
- }
- return res;
- }
- }