modify code

master
algorithmzuo 2 years ago
parent 2f5c5e590d
commit 103256f494

@ -0,0 +1,116 @@
package class_2022_08_5_week;
// 来自学员面试
// nim博弈
// 有a块草莓蛋糕有b块芝士蛋糕两人轮流拿蛋糕
// 每次不管是谁只能选择在草莓蛋糕和芝士蛋糕中拿一种
// 拿的数量在1~m之间随意
// 谁先拿完最后的蛋糕谁赢
// 返回先手赢还是后手赢
public class Code01_Cakes {
// 草莓蛋糕a块
// 巧克力蛋糕b块
// 每次可以在任意一种上拿1~m块
// 返回谁会赢,"先手" or "后手"
public static String[][][] dp = new String[101][101][101];
// 暴力方法
// 为了验证
public static String whoWin1(int a, int b, int m) {
if (m >= Math.max(a, b)) { // nim博弈
return a != b ? "先手" : "后手";
}
if (a == b) {
// 蛋糕一样多
// 先手必输,因为先手不管拿什么,拿多少
// 后手都在另一堆上,拿同样多的蛋糕
// 继续让两堆蛋糕一样多
// 最终先手必输,后手必赢
return "后手";
}
if (dp[a][b][m] != null) {
return dp[a][b][m];
}
String ans = "后手";
for (int pick = 1; pick <= Math.min(a, m); pick++) {
if (whoWin1(a - pick, b, m).equals("后手")) {
ans = "先手";
}
if (ans.equals("先手")) {
break;
}
}
for (int pick = 1; pick <= Math.min(b, m); pick++) {
if (whoWin1(a, b - pick, m).equals("后手")) {
return "先手";
}
if (ans.equals("先手")) {
break;
}
}
dp[a][b][m] = ans;
return ans;
}
// 正式解法
// 时间复杂度O(1)
// 先看nim博弈
public static String whoWin2(int a, int b, int m) {
if (m >= Math.max(a, b)) { // nim博弈
return a != b ? "先手" : "后手";
}
// m < max(a,b)
if (a == b) {
// 蛋糕一样多
// 先手必输,因为先手不管拿什么,拿多少
// 后手都在另一堆上,拿同样多的蛋糕
// 继续让两堆蛋糕一样多
// 最终先手必输,后手必赢
return "后手";
}
// 如果 a != b
// 关注a和b的差值
// 谁最先遇到差值为0谁输
// 那么这就是巴什博奕
// 差值蛋糕数量共rest个。
// 每次从最少取1个最多取m个最后取光的人取胜。
// 如果rest=(m+1)*k + s (s!=0) 那么先手一定必胜
// 因为第一次取走s个
// 接下来无论对手怎么取,
// 先手都能保证取到所有(m+1)倍数的点,
// 那么循环下去一定能取到差值最后一个。
int rest = Math.max(a, b) - Math.min(a, b);
return rest % (m + 1) != 0 ? "先手" : "后手";
}
public static void main(String[] args) {
int V = 23;
System.out.println("测试开始");
int cnt = 0;
int all = (V + 1) * (V + 1) * (V + 1);
for (int a = 0; a <= V; a++) {
for (int b = 0; b <= V; b++) {
for (int m = 0; m <= V; m++) {
String ans1 = whoWin1(a, b, m);
String ans2 = whoWin2(a, b, m);
if (!ans1.equals(ans2)) {
System.out.println("出错了!");
System.out.println("a : " + a);
System.out.println("b : " + b);
System.out.println("m : " + m);
System.out.println("ans1 : " + ans1);
System.out.println("ans2 : " + ans2);
break;
}
cnt++;
if (cnt % 1000 == 0) {
System.out.println("已经测了" + cnt + "组(共" + all + "组)");
}
}
}
}
System.out.println("测试结束");
}
}

@ -0,0 +1,95 @@
package class_2022_08_5_week;
// 给定一个由 '[' ']''(') 组成的字符串
// 请问最少插入多少个括号就能使这个字符串的所有括号左右配对
// 例如当前串是 "([[])",那么插入一个']'即可满足
// 输出最少插入多少个括号
// 测试链接 : https://www.nowcoder.com/practice/e391767d80d942d29e6095a935a5b96b
// 提交如下代码把主类名改成Main可以直接通过
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Code02_MinAddToMatch {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line;
while ((line = br.readLine()) != null) {
System.out.println(minAdd(line.toCharArray()));
}
}
public static int minAdd(char[] s) {
int n = s.length;
int[][] dp = new int[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
dp[i][j] = -1;
}
}
return process(s, 0, s.length - 1, dp);
}
// 让s[l...r]都完美匹配
// 至少需要加几个字符
public static int process(char[] s, int l, int r, int[][] dp) {
// 只有一个字符,不管是什么,要想配对,都需要添加一个字符
if (l == r) {
return 1;
}
// 只有两个字符,
// 如果是()、[],那什么也不需要添加
// 否则都需要添加2个字符
if (l == r - 1) {
if ((s[l] == '(' && s[r] == ')') || (s[l] == '[' && s[r] == ']')) {
return 0;
}
return 2;
}
if (dp[l][r] != -1) {
return dp[l][r];
}
// 重点是如下的过程
// 可能性1先搞定l+1...r然后搞定l
// 比如s[l...r] = ([][]
// 先搞定[][]需要添加0个然后搞定(需要添加1个
// 整体变成([][])搞定
// l....r -> l l+1....r ?
int p1 = 1 + process(s, l + 1, r, dp);
// 可能性2先搞定l...r-1然后搞定r
// 和可能性1同理
// l...r -> ? l...r-1 r
int p2 = 1 + process(s, l, r - 1, dp);
// l( ...r) l+1..r-1
// l[ r] l+1..r-1
// 可能性3s[l]和s[r]天然匹配需要搞定的就是l+1..r-1
// 比如([[),搞定中间的[[,就是最优解了
int p3 = Integer.MAX_VALUE;
if ((s[l] == '(' && s[r] == ')') || (s[l] == '[' && s[r] == ']')) {
p3 = process(s, l + 1, r - 1, dp);
}
// l......r
// l..l l+1..r
// l..l+1 l+2..r
// l...l+2 l+3..r
// 可能性后续可能最优解并不是l....r整体变成最大的嵌套
// 而是,并列关系!
// l....split 先变成合法
// split+1...r 再变成合法
// 是并列的关系!
// 比如(())[[]]
// l...split : (())
// split+1...r : [[]]
// 这种并列关系下,有可能出最优解
// 所以,枚举每一个可能的并列划分点(split)
int ans = Math.min(p1, Math.min(p2, p3));
for (int split = l; split < r; split++) {
ans = Math.min(ans, process(s, l, split, dp) + process(s, split + 1, r, dp));
}
dp[l][r] = ans;
return ans;
}
}

@ -0,0 +1,161 @@
package class_2022_08_5_week;
// 来自Amazon
// 定义一个概念叫"变序最大和"
// "变序最大和"是说一个数组中,每个值都可以减小或者不变,
// 在必须把整体变成严格升序的情况下,得到的最大累加和
// 比如,[1,100,7]变成[1,6,7]时就有变序最大和为14
// 比如,[5,4,9]变成[3,4,9]时就有变序最大和为16
// 比如,[1,4,2]变成[0,1,2]时就有变序最大和为3
// 给定一个数组arr其中所有的数字都是>=0的
// 求arr所有子数组的变序最大和中最大的那个并返回
// 1 <= arr长度 <= 10^6
// 0 <= arr[i] <= 10^6
public class Code03_SubarrayMakeSrotedMaxSum {
// 时间复杂度O(N * V)的方法
// 为了验证
public static long maxSum1(int[] arr) {
int n = arr.length;
int max = 0;
for (int num : arr) {
max = Math.max(max, num);
}
long ans = 0;
long[][] dp = new long[n][max + 1];
for (int i = 0; i < n; i++) {
for (int j = 0; j <= max; j++) {
dp[i][j] = -1;
}
}
for (int i = 0; i < n; i++) {
ans = Math.max(ans, process1(arr, i, arr[i], dp));
}
return ans;
}
public static long process1(int[] arr, int i, int p, long[][] dp) {
if (p <= 0 || i == -1) {
return 0;
}
if (dp[i][p] != -1) {
return dp[i][p];
}
int cur = Math.min(arr[i], p);
long next = process1(arr, i - 1, cur - 1, dp);
long ans = (long) cur + next;
dp[i][p] = ans;
return cur + next;
}
// 正式方法
// 时间复杂度O(N)
public static long maxSum2(int[] arr) {
int n = arr.length;
// 只放下标只要有下标arr可以拿到值
int[] stack = new int[n];
int size = 0;
long[] dp = new long[n];
long ans = 0;
for (int i = 0; i < n; i++) {
// i -> arr[i] 依次把收益!得到!
int curVal = arr[i];
int curIdx = i;
// 20
// 17
while (curVal > 0 && size > 0) {
// 100
// 16
int leftIdx = stack[size - 1];
int leftVal = arr[leftIdx];
if (leftVal >= curVal) {
size--;
} else {
// leftVal < curVal
// 8 20
// 15 17
int idxDiff = curIdx - leftIdx;
int valDiff = curVal - leftVal;
// 12 2
// 8 19 20
// 15 16 17
if (valDiff >= idxDiff) {
dp[i] += sum(curVal, idxDiff) + dp[leftIdx];
curVal = 0;
curIdx = 0;
break;
} else {
// 18 20
// 13 14 15 16 17
// 17 18 19 20
// 16
dp[i] += sum(curVal, idxDiff);
// 16
// 13
curVal -= idxDiff;
curIdx = leftIdx;
size--;
}
}
}
if (curVal > 0) {
dp[i] += sum(curVal, curIdx + 1);
}
stack[size++] = i;
ans = Math.max(ans, dp[i]);
}
return ans;
}
public static long sum(int max, int n) {
n = Math.min(max, n);
return (((long) max * 2 - n + 1) * n) / 2;
}
// 为了验证
public static int[] randomArray(int n, int v) {
int[] ans = new int[n];
for (int i = 0; i < n; i++) {
ans[i] = (int) (Math.random() * v);
}
return ans;
}
public static void main(String[] args) {
int N = 100;
int V = 100;
int testTimes = 50000;
System.out.println("功能测试开始");
for (int i = 0; i < testTimes; i++) {
int n = (int) (Math.random() * N) + 1;
int[] arr = randomArray(n, V);
long ans1 = maxSum1(arr);
long ans2 = maxSum2(arr);
if (ans1 != ans2) {
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println();
System.out.println(ans1);
System.out.println(ans2);
break;
}
}
System.out.println("功能测试结束");
System.out.println("性能测试开始");
int n = 1000000;
int v = 1000000;
System.out.println("数组长度 : " + n);
System.out.println("数值范围 : " + v);
int[] arr = randomArray(n, v);
long start = System.currentTimeMillis();
maxSum2(arr);
long end = System.currentTimeMillis();
System.out.println("运行时间 : " + (end - start) + "毫秒");
System.out.println("性能测试结束");
}
}

@ -0,0 +1,81 @@
package class_2022_08_5_week;
// 给定n棵树和两个长度为n的数组a和b
// i号棵树的初始重量为a[i]i号树每天的增长重量为b[i]
// 你每天最多能砍1棵树这天收益 = 砍的树初始重量 + 砍的树增长到这天的总增重
// 给定m表示你有m天返回m天内你获得的最大收益
// 本题测试链接 : https://zoj.pintia.cn/problem-sets/91827364500/problems/91827367873
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交如下方法把主类名改成Main可以直接通过
//
// 增加的题目:
// 小红定义"漂亮串"为:至少有两个"red"子串
// 例如"redxred"为漂亮串,但"reedred"则不是漂亮串
// 小红想知道长度为n仅包含小写字母的所有字符串中共有多少个不同的漂亮串
// 输入描述:
// 一个正整数n代表字符串长度
// n <= 10^6
// 输出描述:
// 长度为n仅包含小写字母的所有字符串中共有多少个不同的漂亮串结果对10^9 + 7取模
// 本题为课堂临时增加,只有当堂讲述,没有代码实现
// 回答帖子 : https://www.mashibing.com/question/detail/34493
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.Arrays;
public class Code04_DreamCity {
public static int[][] tree = new int[250][2];
public static int[][] dp = new int[250][250];
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StreamTokenizer in = new StreamTokenizer(br);
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
in.nextToken();
int testCases = (int) in.nval;
for (int i = 0; i < testCases; i++) {
in.nextToken();
int n = (int) in.nval;
in.nextToken();
int m = (int) in.nval;
for (int j = 0; j < n; j++) {
in.nextToken();
tree[j][0] = (int) in.nval;
}
for (int j = 0; j < n; j++) {
in.nextToken();
tree[j][1] = (int) in.nval;
}
out.println(maxWeight(n, m));
out.flush();
}
}
// tree[][]
// i棵树初始重量 tree[i][0]
// i棵树每天的增长重量 tree[i][1]
public static int maxWeight(int n, int m) {
Arrays.sort(tree, 0, n, (o1, o2) -> o1[1] - o2[1]);
dp[0][0] = tree[0][0];
for (int i = 1; i < n; i++) {
dp[i][0] = Math.max(dp[i - 1][0], tree[i][0]);
}
for (int j = 1; j < m; j++) {
dp[0][j] = dp[0][j - 1] + tree[0][1];
}
for (int i = 1; i < n; i++) {
for (int j = 1; j < m; j++) {
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1] + tree[i][0] + tree[i][1] * j);
}
}
return dp[n - 1][m - 1];
}
}

@ -1728,3 +1728,53 @@ D居民和E居民之间假设有一条路
输出最小修改次数
第038节 2022年8月第5周流行算法题目解析
来自学员面试
nim博弈
有a块草莓蛋糕有b块芝士蛋糕两人轮流拿蛋糕
每次不管是谁只能选择在草莓蛋糕和芝士蛋糕中拿一种
拿的数量在1~m之间随意
谁先拿完最后的蛋糕谁赢
返回先手赢还是后手赢
给定一个由 '[' ']''(') 组成的字符串
请问最少插入多少个括号就能使这个字符串的所有括号左右配对
例如当前串是 "([[])",那么插入一个']'即可满足
输出最少插入多少个括号
测试链接 : https://www.nowcoder.com/practice/e391767d80d942d29e6095a935a5b96b
来自Amazon
定义一个概念叫"变序最大和"
"变序最大和"是说一个数组中,每个值都可以减小或者不变,
在必须把整体变成严格升序的情况下,得到的最大累加和
比如,[1,100,7]变成[1,6,7]时就有变序最大和为14
比如,[5,4,9]变成[3,4,9]时就有变序最大和为16
比如,[1,4,2]变成[0,1,2]时就有变序最大和为3
给定一个数组arr其中所有的数字都是>=0的
求arr所有子数组的变序最大和中最大的那个并返回
1 <= arr长度 <= 10^6
0 <= arr[i] <= 10^6
给定n棵树和两个长度为n的数组a和b
i号棵树的初始重量为a[i]i号树每天的增长重量为b[i]
你每天最多能砍1棵树这天收益 = 砍的树初始重量 + 砍的树增长到这天的总增重
给定m表示你有m天返回m天内你获得的最大收益
本题测试链接 : https://zoj.pintia.cn/problem-sets/91827364500/problems/91827367873
增加的题目:
小红定义"漂亮串"为:至少有两个"red"子串
例如"redxred"为漂亮串,但"reedred"则不是漂亮串
小红想知道长度为n仅包含小写字母的所有字符串中共有多少个不同的漂亮串
输入描述:
一个正整数n代表字符串长度
n <= 10^6
输出描述:
长度为n仅包含小写字母的所有字符串中共有多少个不同的漂亮串结果对10^9 + 7取模
本题为课堂临时增加,只有当堂讲述,没有代码实现
回答帖子 : https://www.mashibing.com/question/detail/34493

Loading…
Cancel
Save