pull/6/head
左程云 5 years ago
parent b0ec4db667
commit 4184f32000

@ -2,16 +2,19 @@ package class32;
public class Code01_IndexTree {
// 下标从1开始
public static class IndexTree {
private int[] tree;
private int N;
// 0位置弃而不用
public IndexTree(int size) {
N = size;
tree = new int[N + 1];
}
// 1~index 累加和是多少?
public int sum(int index) {
int ret = 0;
while (index > 0) {
@ -21,6 +24,9 @@ public class Code01_IndexTree {
return ret;
}
// index & -index : 提取出index最右侧的1出来
// index : 0011001000
// index & -index : 0000001000
public void add(int index, int d) {
while (index <= N) {
tree[index] += d;

@ -17,6 +17,7 @@ public class Code04_AC2 {
public boolean endUse;
public Node fail;
public Node[] nexts;
public Node() {
endUse = false;
end = null;
@ -39,8 +40,7 @@ public class Code04_AC2 {
for (int i = 0; i < str.length; i++) {
index = str[i] - 'a';
if (cur.nexts[index] == null) {
Node next = new Node();
cur.nexts[index] = next;
cur.nexts[index] = new Node();
}
cur = cur.nexts[index];
}
@ -53,14 +53,12 @@ public class Code04_AC2 {
Node cur = null;
Node cfail = null;
while (!queue.isEmpty()) {
// 当前节点弹出,
// 当前节点的所有后代加入到队列里去,
// 当前节点给它的子去设置fail指针
// cur -> 父亲
// 某个父亲cur
cur = queue.poll();
for (int i = 0; i < 26; i++) { // 所有的路
if (cur.nexts[i] != null) { // 找到所有有效的路
cur.nexts[i].fail = root; //
// cur -> 父亲 i号儿子必须把i号儿子的fail指针设置好
if (cur.nexts[i] != null) { // 如果真的有i号儿子
cur.nexts[i].fail = root;
cfail = cur.fail;
while (cfail != null) {
if (cfail.nexts[i] != null) {
@ -75,6 +73,7 @@ public class Code04_AC2 {
}
}
// 大文章content
public List<String> containWords(String content) {
char[] str = content.toCharArray();
Node cur = root;

@ -29,7 +29,7 @@ public class Hash {
}
System.out.println("=======");
String algorithm = "SHA";
String algorithm = "MD5";
Hash hash = new Hash(algorithm);
String input1 = "zuochengyunzuochengyun1";

@ -164,18 +164,17 @@ public class Code01_CountofRangeSum {
}
public static int countRangeSum2(int[] nums, int lower, int upper) {
// 黑盒,加入数字(前缀和),不去重,可以接受重复数字
// < num , 有几个数?
SizeBalancedTreeSet treeSet = new SizeBalancedTreeSet();
long sum = 0;
int ans = 0;
treeSet.add(0);// 一个数都没有的时候就已经有一个前缀和累加和为0
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
// sum i结尾的时候[lower, upper]
// 之前所有前缀累加和中,有多少累加和落在[sum - upper, sum - lower]
// 查 < sum - lower + 1 a
// 查 ? < sum - upper b
// a - b
// [sum - upper, sum - lower]
// [10, 20] ?
// < 10 ? < 21 ?
long a = treeSet.lessKeySize(sum - lower + 1);
long b = treeSet.lessKeySize(sum - upper);
ans += a - b;

@ -0,0 +1,265 @@
package class37;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedList;
// 本题测试链接https://leetcode.com/problems/queue-reconstruction-by-height/
public class Code04_QueueReconstructionByHeight {
public static int[][] reconstructQueue1(int[][] people) {
int N = people.length;
Unit[] units = new Unit[N];
for (int i = 0; i < N; i++) {
units[i] = new Unit(people[i][0], people[i][1]);
}
Arrays.sort(units, new UnitComparator());
ArrayList<Unit> arrList = new ArrayList<>();
for (Unit unit : units) {
arrList.add(unit.k, unit);
}
int[][] ans = new int[N][2];
int index = 0;
for (Unit unit : arrList) {
ans[index][0] = unit.h;
ans[index++][1] = unit.k;
}
return ans;
}
public static int[][] reconstructQueue2(int[][] people) {
int N = people.length;
Unit[] units = new Unit[N];
for (int i = 0; i < N; i++) {
units[i] = new Unit(people[i][0], people[i][1]);
}
Arrays.sort(units, new UnitComparator());
SBTree tree = new SBTree();
for (int i = 0; i < N; i++) {
tree.insert(units[i].k, i);
}
LinkedList<Integer> allIndexes = tree.allIndexes();
int[][] ans = new int[N][2];
int index = 0;
for (Integer arri : allIndexes) {
ans[index][0] = units[arri].h;
ans[index++][1] = units[arri].k;
}
return ans;
}
public static class Unit {
public int h;
public int k;
public Unit(int height, int greater) {
h = height;
k = greater;
}
}
public static class UnitComparator implements Comparator<Unit> {
@Override
public int compare(Unit o1, Unit o2) {
return o1.h != o2.h ? (o2.h - o1.h) : (o1.k - o2.k);
}
}
public static class SBTNode {
public int value;
public SBTNode l;
public SBTNode r;
public int size;
public SBTNode(int arrIndex) {
value = arrIndex;
size = 1;
}
}
public static class SBTree {
private SBTNode root;
private SBTNode rightRotate(SBTNode cur) {
SBTNode leftNode = cur.l;
cur.l = leftNode.r;
leftNode.r = cur;
leftNode.size = cur.size;
cur.size = (cur.l != null ? cur.l.size : 0) + (cur.r != null ? cur.r.size : 0) + 1;
return leftNode;
}
private SBTNode leftRotate(SBTNode cur) {
SBTNode rightNode = cur.r;
cur.r = rightNode.l;
rightNode.l = cur;
rightNode.size = cur.size;
cur.size = (cur.l != null ? cur.l.size : 0) + (cur.r != null ? cur.r.size : 0) + 1;
return rightNode;
}
private SBTNode maintain(SBTNode cur) {
if (cur == null) {
return null;
}
int leftSize = cur.l != null ? cur.l.size : 0;
int leftLeftSize = cur.l != null && cur.l.l != null ? cur.l.l.size : 0;
int leftRightSize = cur.l != null && cur.l.r != null ? cur.l.r.size : 0;
int rightSize = cur.r != null ? cur.r.size : 0;
int rightLeftSize = cur.r != null && cur.r.l != null ? cur.r.l.size : 0;
int rightRightSize = cur.r != null && cur.r.r != null ? cur.r.r.size : 0;
if (leftLeftSize > rightSize) {
cur = rightRotate(cur);
cur.r = maintain(cur.r);
cur = maintain(cur);
} else if (leftRightSize > rightSize) {
cur.l = leftRotate(cur.l);
cur = rightRotate(cur);
cur.l = maintain(cur.l);
cur.r = maintain(cur.r);
cur = maintain(cur);
} else if (rightRightSize > leftSize) {
cur = leftRotate(cur);
cur.l = maintain(cur.l);
cur = maintain(cur);
} else if (rightLeftSize > leftSize) {
cur.r = rightRotate(cur.r);
cur = leftRotate(cur);
cur.l = maintain(cur.l);
cur.r = maintain(cur.r);
cur = maintain(cur);
}
return cur;
}
private SBTNode insert(SBTNode root, int index, SBTNode cur) {
if (root == null) {
return cur;
}
root.size++;
int leftAndHeadSize = (root.l != null ? root.l.size : 0) + 1;
if (index < leftAndHeadSize) {
root.l = insert(root.l, index, cur);
} else {
root.r = insert(root.r, index - leftAndHeadSize, cur);
}
root = maintain(root);
return root;
}
private SBTNode get(SBTNode root, int index) {
int leftSize = root.l != null ? root.l.size : 0;
if (index < leftSize) {
return get(root.l, index);
} else if (index == leftSize) {
return root;
} else {
return get(root.r, index - leftSize - 1);
}
}
private void process(SBTNode head, LinkedList<Integer> indexes) {
if (head == null) {
return;
}
process(head.l, indexes);
indexes.addLast(head.value);
process(head.r, indexes);
}
public void insert(int index, int value) {
SBTNode cur = new SBTNode(value);
if (root == null) {
root = cur;
} else {
if (index <= root.size) {
root = insert(root, index, cur);
}
}
}
public int get(int index) {
SBTNode ans = get(root, index);
return ans.value;
}
public LinkedList<Integer> allIndexes() {
LinkedList<Integer> indexes = new LinkedList<>();
process(root, indexes);
return indexes;
}
}
// 通过以下这个测试,
// 可以很明显的看到LinkedList的插入和get效率不如SBTree
// LinkedList需要找到index所在的位置之后才能插入或者读取时间复杂度O(N)
// SBTree是平衡搜索二叉树所以插入或者读取时间复杂度都是O(logN)
public static void main(String[] args) {
// 功能测试
int test = 10000;
int max = 1000000;
boolean pass = true;
LinkedList<Integer> list = new LinkedList<>();
SBTree sbtree = new SBTree();
for (int i = 0; i < test; i++) {
int randomIndex = (int) (Math.random() * (i + 1));
int randomValue = (int) (Math.random() * (max + 1));
list.add(randomIndex, randomValue);
sbtree.insert(randomIndex, randomValue);
}
for (int i = 0; i < test; i++) {
if (list.get(i) != sbtree.get(i)) {
pass = false;
break;
}
}
System.out.println("功能测试是否通过 : " + pass);
// 性能测试
test = 50000;
list = new LinkedList<>();
sbtree = new SBTree();
long start = 0;
long end = 0;
start = System.currentTimeMillis();
for (int i = 0; i < test; i++) {
int randomIndex = (int) (Math.random() * (i + 1));
int randomValue = (int) (Math.random() * (max + 1));
list.add(randomIndex, randomValue);
}
end = System.currentTimeMillis();
System.out.println("LinkedList插入总时长(毫秒) " + (end - start));
start = System.currentTimeMillis();
for (int i = 0; i < test; i++) {
int randomIndex = (int) (Math.random() * (i + 1));
list.get(randomIndex);
}
end = System.currentTimeMillis();
System.out.println("LinkedList读取总时长(毫秒) : " + (end - start));
start = System.currentTimeMillis();
for (int i = 0; i < test; i++) {
int randomIndex = (int) (Math.random() * (i + 1));
int randomValue = (int) (Math.random() * (max + 1));
sbtree.insert(randomIndex, randomValue);
}
end = System.currentTimeMillis();
System.out.println("SBTree插入总时长(毫秒) : " + (end - start));
start = System.currentTimeMillis();
for (int i = 0; i < test; i++) {
int randomIndex = (int) (Math.random() * (i + 1));
sbtree.get(randomIndex);
}
end = System.currentTimeMillis();
System.out.println("SBTree读取总时长(毫秒) : " + (end - start));
}
}

@ -0,0 +1,47 @@
package class38;
public class Code01_AppleMinBags {
public static int minBags(int apple) {
if (apple < 0) {
return -1;
}
int bag6 = -1;
int bag8 = apple / 8;
int rest = apple - 8 * bag8;
while (bag8 >= 0 && rest < 24) {
int restUse6 = minBagBase6(rest);
if (restUse6 != -1) {
bag6 = restUse6;
break;
}
rest = apple - 8 * (--bag8);
}
return bag6 == -1 ? -1 : bag6 + bag8;
}
// 如果剩余苹果rest可以被装6个苹果的袋子搞定返回袋子数量
// 不能搞定返回-1
public static int minBagBase6(int rest) {
return rest % 6 == 0 ? (rest / 6) : -1;
}
public static int minBagAwesome(int apple) {
if ((apple & 1) != 0) { // 如果是奇数,返回-1
return -1;
}
if (apple < 18) {
return apple == 0 ? 0 : (apple == 6 || apple == 8) ? 1
: (apple == 12 || apple == 14 || apple == 16) ? 2 : -1;
}
return (apple - 18) / 8 + 3;
}
public static void main(String[] args) {
for(int apple = 1; apple < 100;apple++) {
System.out.println(apple + " : "+ minBags(apple));
}
}
}

@ -0,0 +1,45 @@
package class38;
public class Code02_EatGrass {
// n份青草放在一堆
// 先手后手都绝顶聪明
// string "先手" "后手"
public static String winner1(int n) {
// 0 1 2 3 4
// 后 先 后 先 先
if (n < 5) { // base case
return (n == 0 || n == 2) ? "后手" : "先手";
}
// n >= 5 时
int base = 1; // 当前先手决定吃的草数
// 当前是先手在选
while (base <= n) {
// 当前一共n份草先手吃掉的是base份n - base 是留给后手的草
// 母过程 先手 在子过程里是 后手
if (winner1(n - base).equals("后手")) {
return "先手";
}
if (base > n / 4) { // 防止base*4之后溢出
break;
}
base *= 4;
}
return "后手";
}
public static String winner2(int n) {
if (n % 5 == 0 || n % 5 == 2) {
return "后手";
} else {
return "先手";
}
}
public static void main(String[] args) {
for (int i = 0; i <= 50; i++) {
System.out.println(i + " : " + winner1(i));
}
}
}

@ -0,0 +1,41 @@
package class38;
public class Code03_MSumToN {
public static boolean isMSum1(int num) {
for (int i = 1; i <= num; i++) {
int sum = i;
for (int j = i + 1; j <= num; j++) {
if (sum + j > num) {
break;
}
if (sum + j == num) {
return true;
}
sum += j;
}
}
return false;
}
public static boolean isMSum2(int num) {
if (num < 3) {
return false;
}
return (num & (num - 1)) != 0;
}
public static void main(String[] args) {
for (int num = 1; num < 200; num++) {
System.out.println(num + " : " + isMSum1(num));
}
System.out.println("test begin");
for (int num = 1; num < 5000; num++) {
if (isMSum1(num) != isMSum2(num)) {
System.out.println("Oops!");
}
}
System.out.println("test end");
}
}

@ -0,0 +1,131 @@
package class38;
public class Code04_MoneyProblem {
// int[] d d[i]i号怪兽的武力
// int[] p p[i]i号怪兽要求的钱
// ability 当前你所具有的能力
// index 来到了第index个怪兽的面前
// 目前你的能力是ability你来到了index号怪兽的面前如果要通过后续所有的怪兽
// 请返回需要花的最少钱数
public static long process(int[] d, int[] p, int ability, int index) {
if (index == d.length) {
return 0;
}
if (ability < d[index]) {
return p[index] + process(d, p, ability + d[index], index + 1);
} else { // 可以贿赂,也可以不贿赂
return
Math.min(
p[index] + process(d, p, ability + d[index], index + 1),
process(d, p, ability, index + 1)
);
}
}
public static long func1(int[] d, int[] p) {
return process(d, p, 0, 0);
}
public static long func2(int[] d, int[] p) {
int sum = 0;
for (int num : d) {
sum += num;
}
long[][] dp = new long[d.length + 1][sum + 1];
for (int i = 0; i <= sum; i++) {
dp[0][i] = 0;
}
for (int cur = d.length - 1; cur >= 0; cur--) {
for (int hp = 0; hp <= sum; hp++) {
// 如果这种情况发生那么这个hp必然是递归过程中不会出现的状态
// 既然动态规划是尝试过程的优化,尝试过程碰不到的状态,不必计算
if (hp + d[cur] > sum) {
continue;
}
if (hp < d[cur]) {
dp[cur][hp] = p[cur] + dp[cur + 1][hp + d[cur]];
} else {
dp[cur][hp] = Math.min(p[cur] + dp[cur + 1][hp + d[cur]], dp[cur + 1][hp]);
}
}
}
return dp[0][0];
}
public static long func3(int[] d, int[] p) {
int sum = 0;
for (int num : p) {
sum += num;
}
// dp[i][j]含义:
// 能经过0i的怪兽且花钱为j花钱的严格等于j时的武力值最大是多少
// 如果dp[i][j]==-1表示经过0i的怪兽花钱为j是无法通过的或者之前的钱怎么组合也得不到正好为j的钱数
int[][] dp = new int[d.length][sum + 1];
for (int i = 0; i < dp.length; i++) {
for (int j = 0; j <= sum; j++) {
dp[i][j] = -1;
}
}
// 经过0i的怪兽花钱数一定为p[0]达到武力值d[0]的地步。其他第0行的状态一律是无效的
dp[0][p[0]] = d[0];
for (int i = 1; i < d.length; i++) {
for (int j = 0; j <= sum; j++) {
// 可能性一,为当前怪兽花钱
// 存在条件:
// j - p[i]要不越界并且在钱数为j - p[i]时要能通过0i-1的怪兽并且钱数组合是有效的。
if (j >= p[i] && dp[i - 1][j - p[i]] != -1) {
dp[i][j] = dp[i - 1][j - p[i]] + d[i];
}
// 可能性二,不为当前怪兽花钱
// 存在条件:
// 0~i-1怪兽在花钱为j的情况下能保证通过当前i位置的怪兽
if (dp[i - 1][j] >= d[i]) {
// 两种可能性中,选武力值最大的
dp[i][j] = Math.max(dp[i][j], dp[i - 1][j]);
}
}
}
int ans = 0;
// dp表最后一行上dp[N-1][j]代表:
// 能经过0N-1的怪兽且花钱为j花钱的严格等于j时的武力值最大是多少
// 那么最后一行上,最左侧的不为-1的列数(j),就是答案
for (int j = 0; j <= sum; j++) {
if (dp[d.length - 1][j] != -1) {
ans = j;
break;
}
}
return ans;
}
public static int[][] generateTwoRandomArray(int len, int value) {
int size = (int) (Math.random() * len) + 1;
int[][] arrs = new int[2][size];
for (int i = 0; i < size; i++) {
arrs[0][i] = (int) (Math.random() * value) + 1;
arrs[1][i] = (int) (Math.random() * value) + 1;
}
return arrs;
}
public static void main(String[] args) {
int len = 10;
int value = 20;
int testTimes = 1000000;
for (int i = 0; i < testTimes; i++) {
int[][] arrs = generateTwoRandomArray(len, value);
int[] d = arrs[0];
int[] p = arrs[1];
long ans1 = func1(d, p);
long ans2 = func2(d, p);
long ans3 = func3(d, p);
if (ans1 != ans2 || ans2 != ans3) {
System.out.println("oops!");
}
}
}
}

@ -0,0 +1,66 @@
package class39;
public class Code01_DifferentBTNum {
// k(0) = 1, k(1) = 1
//
// k(n) = k(0) * k(n - 1) + k(1) * k(n - 2) + ... + k(n - 2) * k(1) + k(n - 1) * k(0)
// 或者
// k(n) = c(2n, n) / (n + 1)
// 或者
// k(n) = c(2n, n) - c(2n, n-1)
public static long num1(int N) {
if (N < 0) {
return 0;
}
if (N < 2) {
return 1;
}
long[] dp = new long[N + 1];
dp[0] = 1;
dp[1] = 1;
for (int i = 2; i <= N; i++) {
for (int leftSize = 0; leftSize < i; leftSize++) {
dp[i] += dp[leftSize] * dp[i - 1 - leftSize];
}
}
return dp[N];
}
public static long num2(int N) {
if (N < 0) {
return 0;
}
if (N < 2) {
return 1;
}
long a = 1;
long b = 1;
for (int i = 1, j = N + 1; i <= N; i++, j++) {
a *= i;
b *= j;
long gcd = gcd(a, b);
a /= gcd;
b /= gcd;
}
return (b / a) / (N + 1);
}
public static long gcd(long m, long n) {
return n == 0 ? m : gcd(n, m % n);
}
public static void main(String[] args) {
System.out.println("test begin");
for (int i = 0; i < 15; i++) {
long ans1 = num1(i);
long ans2 = num2(i);
if (ans1 != ans2) {
System.out.println("Oops!");
}
}
System.out.println("test finish");
}
}

@ -0,0 +1,92 @@
package class39;
import java.util.LinkedList;
public class Code02_10Ways {
public static long ways1(int N) {
int zero = N;
int one = N;
LinkedList<Integer> path = new LinkedList<>();
LinkedList<LinkedList<Integer>> ans = new LinkedList<>();
process(zero, one, path, ans);
long count = 0;
for (LinkedList<Integer> cur : ans) {
int status = 0;
for (Integer num : cur) {
if (num == 0) {
status++;
} else {
status--;
}
if (status < 0) {
break;
}
}
if (status == 0) {
count++;
}
}
return count;
}
public static void process(int zero, int one, LinkedList<Integer> path, LinkedList<LinkedList<Integer>> ans) {
if (zero == 0 && one == 0) {
LinkedList<Integer> cur = new LinkedList<>();
for (Integer num : path) {
cur.add(num);
}
ans.add(cur);
} else {
if (zero == 0) {
path.addLast(1);
process(zero, one - 1, path, ans);
path.removeLast();
} else if (one == 0) {
path.addLast(0);
process(zero - 1, one, path, ans);
path.removeLast();
} else {
path.addLast(1);
process(zero, one - 1, path, ans);
path.removeLast();
path.addLast(0);
process(zero - 1, one, path, ans);
path.removeLast();
}
}
}
public static long ways2(int N) {
if (N < 0) {
return 0;
}
if (N < 2) {
return 1;
}
long a = 1;
long b = 1;
long limit = N << 1;
for (long i = 1; i <= limit; i++) {
if (i <= N) {
a *= i;
} else {
b *= i;
}
}
return (b / a) / (N + 1);
}
public static void main(String[] args) {
System.out.println("test begin");
for (int i = 0; i < 10; i++) {
long ans1 = ways1(i);
long ans2 = ways2(i);
if (ans1 != ans2) {
System.out.println("Oops!");
}
}
System.out.println("test finish");
}
}

@ -0,0 +1,44 @@
package class39;
public class Code03_RotateMatrix {
public static void rotate(int[][] matrix) {
int a = 0;
int b = 0;
int c = matrix.length - 1;
int d = matrix[0].length - 1;
while (a < c) {
rotateEdge(matrix, a++, b++, c--, d--);
}
}
public static void rotateEdge(int[][] m, int a, int b, int c, int d) {
int tmp = 0;
for (int i = 0; i < d - b; i++) {
tmp = m[a][b + i];
m[a][b + i] = m[c - i][b];
m[c - i][b] = m[c][d - i];
m[c][d - i] = m[a + i][d];
m[a + i][d] = tmp;
}
}
public static void printMatrix(int[][] matrix) {
for (int i = 0; i != matrix.length; i++) {
for (int j = 0; j != matrix[0].length; j++) {
System.out.print(matrix[i][j] + " ");
}
System.out.println();
}
}
public static void main(String[] args) {
int[][] matrix = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 }, { 13, 14, 15, 16 } };
printMatrix(matrix);
rotate(matrix);
System.out.println("=========");
printMatrix(matrix);
}
}

@ -0,0 +1,53 @@
package class39;
public class Code04_PrintMatrixSpiralOrder {
public static void spiralOrderPrint(int[][] matrix) {
int tR = 0;
int tC = 0;
int dR = matrix.length - 1;
int dC = matrix[0].length - 1;
while (tR <= dR && tC <= dC) {
printEdge(matrix, tR++, tC++, dR--, dC--);
}
}
public static void printEdge(int[][] m, int tR, int tC, int dR, int dC) {
if (tR == dR) {
for (int i = tC; i <= dC; i++) {
System.out.print(m[tR][i] + " ");
}
} else if (tC == dC) {
for (int i = tR; i <= dR; i++) {
System.out.print(m[i][tC] + " ");
}
} else {
int curC = tC;
int curR = tR;
while (curC != dC) {
System.out.print(m[tR][curC] + " ");
curC++;
}
while (curR != dR) {
System.out.print(m[curR][dC] + " ");
curR++;
}
while (curC != tC) {
System.out.print(m[dR][curC] + " ");
curC--;
}
while (curR != tR) {
System.out.print(m[curR][tC] + " ");
curR--;
}
}
}
public static void main(String[] args) {
int[][] matrix = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 },
{ 13, 14, 15, 16 } };
spiralOrderPrint(matrix);
}
}

@ -0,0 +1,43 @@
package class39;
public class Code05_ZigZagPrintMatrix {
public static void printMatrixZigZag(int[][] matrix) {
int tR = 0;
int tC = 0;
int dR = 0;
int dC = 0;
int endR = matrix.length - 1;
int endC = matrix[0].length - 1;
boolean fromUp = false;
while (tR != endR + 1) {
printLevel(matrix, tR, tC, dR, dC, fromUp);
tR = tC == endC ? tR + 1 : tR;
tC = tC == endC ? tC : tC + 1;
dC = dR == endR ? dC + 1 : dC;
dR = dR == endR ? dR : dR + 1;
fromUp = !fromUp;
}
System.out.println();
}
public static void printLevel(int[][] m, int tR, int tC, int dR, int dC,
boolean f) {
if (f) {
while (tR != dR + 1) {
System.out.print(m[tR++][tC--] + " ");
}
} else {
while (dR != tR - 1) {
System.out.print(m[dR--][dC++] + " ");
}
}
}
public static void main(String[] args) {
int[][] matrix = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } };
printMatrixZigZag(matrix);
}
}

@ -0,0 +1,46 @@
package class39;
public class Code06_PrintStar {
public static void printStar(int N) {
int leftUp = 0;
int rightDown = N - 1;
char[][] m = new char[N][N];
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
m[i][j] = ' ';
}
}
while (leftUp <= rightDown) {
set(m, leftUp, rightDown);
leftUp += 2;
rightDown -= 2;
}
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
System.out.print(m[i][j] + " ");
}
System.out.println();
}
}
public static void set(char[][] m, int leftUp, int rightDown) {
for (int col = leftUp; col <= rightDown; col++) {
m[leftUp][col] = '*';
}
for (int row = leftUp + 1; row <= rightDown; row++) {
m[row][rightDown] = '*';
}
for (int col = rightDown - 1; col > leftUp; col--) {
m[rightDown][col] = '*';
}
for (int row = rightDown - 1; row > leftUp + 1; row--) {
m[row][leftUp + 1] = '*';
}
}
public static void main(String[] args) {
printStar(8);
}
}

@ -0,0 +1,91 @@
package class40;
public class Code01_LongestSumSubArrayLengthInPositiveArray {
public static int getMaxLength(int[] arr, int K) {
if (arr == null || arr.length == 0 || K <= 0) {
return 0;
}
int left = 0;
int right = 0;
int sum = arr[0];
int len = 0;
while (right < arr.length) {
if (sum == K) {
len = Math.max(len, right - left + 1);
sum -= arr[left++];
} else if (sum < K) {
right++;
if (right == arr.length) {
break;
}
sum += arr[right];
} else {
sum -= arr[left++];
}
}
return len;
}
// for test
public static int right(int[] arr, int K) {
int max = 0;
for (int i = 0; i < arr.length; i++) {
for (int j = i; j < arr.length; j++) {
if (valid(arr, i, j, K)) {
max = Math.max(max, j - i + 1);
}
}
}
return max;
}
// for test
public static boolean valid(int[] arr, int L, int R, int K) {
int sum = 0;
for (int i = L; i <= R; i++) {
sum += arr[i];
}
return sum == K;
}
// for test
public static int[] generatePositiveArray(int size, int value) {
int[] ans = new int[size];
for (int i = 0; i != size; i++) {
ans[i] = (int) (Math.random() * value) + 1;
}
return ans;
}
// for test
public static void printArray(int[] arr) {
for (int i = 0; i != arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int len = 50;
int value = 100;
int testTime = 500000;
System.out.println("test begin");
for (int i = 0; i < testTime; i++) {
int[] arr = generatePositiveArray(len, value);
int K = (int) (Math.random() * value) + 1;
int ans1 = getMaxLength(arr, K);
int ans2 = right(arr, K);
if (ans1 != ans2) {
System.out.println("Oops!");
printArray(arr);
System.out.println("K : " + K);
System.out.println(ans1);
System.out.println(ans2);
break;
}
}
System.out.println("test end");
}
}

@ -0,0 +1,90 @@
package class40;
import java.util.HashMap;
public class Code02_LongestSumSubArrayLength {
public static int maxLength(int[] arr, int k) {
if (arr == null || arr.length == 0) {
return 0;
}
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
map.put(0, -1); // important
int len = 0;
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
if (map.containsKey(sum - k)) {
len = Math.max(i - map.get(sum - k), len);
}
if (!map.containsKey(sum)) {
map.put(sum, i);
}
}
return len;
}
// for test
public static int right(int[] arr, int K) {
int max = 0;
for (int i = 0; i < arr.length; i++) {
for (int j = i; j < arr.length; j++) {
if (valid(arr, i, j, K)) {
max = Math.max(max, j - i + 1);
}
}
}
return max;
}
// for test
public static boolean valid(int[] arr, int L, int R, int K) {
int sum = 0;
for (int i = L; i <= R; i++) {
sum += arr[i];
}
return sum == K;
}
// for test
public static int[] generateRandomArray(int size, int value) {
int[] ans = new int[(int) (Math.random() * size) + 1];
for (int i = 0; i < ans.length; i++) {
ans[i] = (int) (Math.random() * value) - (int) (Math.random() * value);
}
return ans;
}
// for test
public static void printArray(int[] arr) {
for (int i = 0; i != arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int len = 50;
int value = 100;
int testTime = 500000;
System.out.println("test begin");
for (int i = 0; i < testTime; i++) {
int[] arr = generateRandomArray(len, value);
int K = (int) (Math.random() * value) - (int) (Math.random() * value);
int ans1 = maxLength(arr, K);
int ans2 = right(arr, K);
if (ans1 != ans2) {
System.out.println("Oops!");
printArray(arr);
System.out.println("K : " + K);
System.out.println(ans1);
System.out.println(ans2);
break;
}
}
System.out.println("test end");
}
}

@ -0,0 +1,105 @@
package class40;
public class Code03_LongestLessSumSubArrayLength {
public static int maxLengthAwesome(int[] arr, int k) {
if (arr == null || arr.length == 0) {
return 0;
}
int[] minSums = new int[arr.length];
int[] minSumEnds = new int[arr.length];
minSums[arr.length - 1] = arr[arr.length - 1];
minSumEnds[arr.length - 1] = arr.length - 1;
for (int i = arr.length - 2; i >= 0; i--) {
if (minSums[i + 1] < 0) {
minSums[i] = arr[i] + minSums[i + 1];
minSumEnds[i] = minSumEnds[i + 1];
} else {
minSums[i] = arr[i];
minSumEnds[i] = i;
}
}
int end = 0;
int sum = 0;
int res = 0;
// i是窗口的最左的位置end扩出来的最右有效块儿的最后一个位置的再下一个位置
// end也是下一块儿的开始位置
// 窗口:[i~end)
for (int i = 0; i < arr.length; i++) {
// while循环结束之后
// 1) 如果以i开头的情况下累加和<=k的最长子数组是arr[i..end-1]看看这个子数组长度能不能更新res
// 2) 如果以i开头的情况下累加和<=k的最长子数组比arr[i..end-1]短更新还是不更新res都不会影响最终结果
while (end < arr.length && sum + minSums[end] <= k) {
sum += minSums[end];
end = minSumEnds[end] + 1;
}
res = Math.max(res, end - i);
if (end > i) { // 窗口内还有数 [i~end) [4,4)
sum -= arr[i];
} else { // 窗口内已经没有数了说明从i开头的所有子数组累加和都不可能<=k
end = i + 1;
}
}
return res;
}
public static int maxLength(int[] arr, int k) {
int[] h = new int[arr.length + 1];
int sum = 0;
h[0] = sum;
for (int i = 0; i != arr.length; i++) {
sum += arr[i];
h[i + 1] = Math.max(sum, h[i]);
}
sum = 0;
int res = 0;
int pre = 0;
int len = 0;
for (int i = 0; i != arr.length; i++) {
sum += arr[i];
pre = getLessIndex(h, sum - k);
len = pre == -1 ? 0 : i - pre + 1;
res = Math.max(res, len);
}
return res;
}
public static int getLessIndex(int[] arr, int num) {
int low = 0;
int high = arr.length - 1;
int mid = 0;
int res = -1;
while (low <= high) {
mid = (low + high) / 2;
if (arr[mid] >= num) {
res = mid;
high = mid - 1;
} else {
low = mid + 1;
}
}
return res;
}
// for test
public static int[] generateRandomArray(int len, int maxValue) {
int[] res = new int[len];
for (int i = 0; i != res.length; i++) {
res[i] = (int) (Math.random() * maxValue) - (maxValue / 3);
}
return res;
}
public static void main(String[] args) {
System.out.println("test begin");
for (int i = 0; i < 10000000; i++) {
int[] arr = generateRandomArray(10, 20);
int k = (int) (Math.random() * 20) - 5;
if (maxLengthAwesome(arr, k) != maxLength(arr, k)) {
System.out.println("Oops!");
}
}
System.out.println("test finish");
}
}

@ -0,0 +1,152 @@
package class40;
import java.util.TreeMap;
public class Code04_AvgLessEqualValueLongestSubarray {
// 暴力解时间复杂度O(N^3),用于做对数器
public static int ways1(int[] arr, int v) {
int ans = 0;
for (int L = 0; L < arr.length; L++) {
for (int R = L; R < arr.length; R++) {
int sum = 0;
int k = R - L + 1;
for (int i = L; i <= R; i++) {
sum += arr[i];
}
double avg = (double) sum / (double) k;
if (avg <= v) {
ans = Math.max(ans, k);
}
}
}
return ans;
}
// 想实现的解法2时间复杂度O(N*logN)
public static int ways2(int[] arr, int v) {
if (arr == null || arr.length == 0) {
return 0;
}
TreeMap<Integer, Integer> origins = new TreeMap<>();
int ans = 0;
int modify = 0;
for (int i = 0; i < arr.length; i++) {
int p1 = arr[i] <= v ? 1 : 0;
int p2 = 0;
int querry = -arr[i] - modify;
if (origins.floorKey(querry) != null) {
p2 = i - origins.get(origins.floorKey(querry)) + 1;
}
ans = Math.max(ans, Math.max(p1, p2));
int curOrigin = -modify - v;
if (origins.floorKey(curOrigin) == null) {
origins.put(curOrigin, i);
}
modify += arr[i] - v;
}
return ans;
}
// 想实现的解法3时间复杂度O(N)
public static int ways3(int[] arr, int v) {
if (arr == null || arr.length == 0) {
return 0;
}
for (int i = 0; i < arr.length; i++) {
arr[i] -= v;
}
return maxLengthAwesome(arr, 0);
}
// 找到数组中累加和<=k的最长子数组
public static int maxLengthAwesome(int[] arr, int k) {
int N = arr.length;
int[] sums = new int[N];
int[] ends = new int[N];
sums[N - 1] = arr[N - 1];
ends[N - 1] = N - 1;
for (int i = N - 2; i >= 0; i--) {
if (sums[i + 1] < 0) {
sums[i] = arr[i] + sums[i + 1];
ends[i] = ends[i + 1];
} else {
sums[i] = arr[i];
ends[i] = i;
}
}
int end = 0;
int sum = 0;
int res = 0;
for (int i = 0; i < N; i++) {
while (end < N && sum + sums[end] <= k) {
sum += sums[end];
end = ends[end] + 1;
}
res = Math.max(res, end - i);
if (end > i) {
sum -= arr[i];
} else {
end = i + 1;
}
}
return res;
}
// 用于测试
public static int[] randomArray(int maxLen, int maxValue) {
int len = (int) (Math.random() * maxLen) + 1;
int[] ans = new int[len];
for (int i = 0; i < len; i++) {
ans[i] = (int) (Math.random() * maxValue);
}
return ans;
}
// 用于测试
public static int[] copyArray(int[] arr) {
int[] ans = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
ans[i] = arr[i];
}
return ans;
}
// 用于测试
public static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
// 用于测试
public static void main(String[] args) {
System.out.println("测试开始");
int maxLen = 20;
int maxValue = 100;
int testTime = 500000;
for (int i = 0; i < testTime; i++) {
int[] arr = randomArray(maxLen, maxValue);
int value = (int) (Math.random() * maxValue);
int[] arr1 = copyArray(arr);
int[] arr2 = copyArray(arr);
int[] arr3 = copyArray(arr);
int ans1 = ways1(arr1, value);
int ans2 = ways2(arr2, value);
int ans3 = ways3(arr3, value);
if (ans1 != ans2 || ans1 != ans3) {
System.out.println("测试出错!");
System.out.print("测试数组:");
printArray(arr);
System.out.println("子数组平均值不小于 " + value);
System.out.println("方法1得到的最大长度" + ans1);
System.out.println("方法2得到的最大长度" + ans2);
System.out.println("方法3得到的最大长度" + ans3);
System.out.println("=========================");
}
}
System.out.println("测试结束");
}
}

@ -0,0 +1,179 @@
package class41;
import java.util.*;
public class Code01_NiuNiuSplitField {
// arr都是正数0~i切的时候怎么切最优最优的结论生成一个数组返回
public static int[] getAnswers(int[] arr) {
if (arr == null || arr.length < 1) {
return null;
}
// ans[i] 代表的含义:
// arr[0..i]切两块的各种方案中,哪一种方案能让左右两部份的累加和最小值,尽量大
int[] ans = new int[arr.length];
ans[0] = 0;
int best = 0;// 左 0..0 右 无
for (int i = 1; i < arr.length; i++) {
while (best < i - 1 && getSplit(arr, best, i) < getSplit(arr, best + 1, i)) {
best++;
}
ans[i] = getSplit(arr, best, i);
}
return ans;
}
public static int getSum(int[] arr, int i, int j) {
return 0;
}
// arr[0..j] 如果我把切的这一刀定在m位置左0..m右m+1..j
public static int getSplit(int[] arr, int m, int j) {
return 0;
}
public static int maxMinSumIn16(int[][] matrix) {
if (matrix == null || matrix.length < 4 || matrix[0].length < 4) {
return 0;
}
// help[i][j] 含义,左上角为(0,0) 右下角为(i,j)的子矩阵累加和是多少
int[][] help = generateSumRecord(matrix);
// 通过help得到任何子矩阵的累加和
int col = matrix[0].length;
int res = Integer.MIN_VALUE;
// 3个for循环在暴力枚举纵向三刀
for (int c1 = 0; c1 < col - 3; c1++) {
for (int c2 = c1 + 1; c2 < col - 2; c2++) {
for (int c3 = c2 + 1; c3 < col - 1; c3++) {
// c1 c2 c3
res = Math.max(res, getBestDicision(help, c1, c2, c3));
}
}
}
return res;
}
// record[i][j] 左上角点0,0 有效角点i、j , 该矩阵的累加和
public static int[][] generateSumRecord(int[][] matrix) {
int row = matrix.length;
int col = matrix[0].length;
int[][] record = new int[row][col];
record[0][0] = matrix[0][0];
for (int i = 1; i < row; i++) {
record[i][0] = record[i - 1][0] + matrix[i][0];
}
for (int j = 1; j < col; j++) {
record[0][j] = record[0][j - 1] + matrix[0][j];
}
for (int i = 1; i < row; i++) {
for (int j = 1; j < col; j++) {
record[i][j] = record[i][j - 1] + record[i - 1][j] - record[i - 1][j - 1] + matrix[i][j];
}
}
return record;
}
// 忘掉原来的矩阵只使用help
// 竖着三刀固定c1c2c3
// 所有横着切三刀的可能性中,那种切法能得到最好的结果,把结果返回
public static int getBestDicision(int[][] help, int c1, int c2, int c3) {
// 0i 切一刀出来八块最优指标up[i]
int[] up = getUpSplitArray(help, c1, c2, c3); // 0行~i行 切一刀分8块min max
// i ~ N-1 切一刀出来八块最优指标down[i]
int[] down = getDownSplitArray(help, c1, c2, c3); // i~n-1 切一刀
int res = Integer.MIN_VALUE;
for (int mid = 1; mid < help.length - 2; mid++) {
res = Math.max(res, Math.min(up[mid], down[mid + 1]));
}
return res;
}
public static int value(int[][] record, int c1, int c2, int c3, int prow, int crow) {
int value1 = area(record, prow, 0, crow, c1);
int value2 = area(record, prow, c1 + 1, crow, c2);
int value3 = area(record, prow, c2 + 1, crow, c3);
int value4 = area(record, prow, c3 + 1, crow, record[0].length - 1);
return Math.min(Math.min(value1, value2), Math.min(value3, value4));
}
public static int area(int[][] record, int i1, int j1, int i2, int j2) {
int all = record[i2][j2];
int left = j1 > 0 ? record[i2][j1 - 1] : 0;
int up = i1 > 0 ? record[i1 - 1][j2] : 0;
int makeUp = (i1 > 0 && j1 > 0) ? record[i1 - 1][j1 - 1] : 0;
return all - left - up + makeUp;
}
public static int[] getUpSplitArray(int[][] record, int c1, int c2, int c3) {
int size = record.length;
int[] up = new int[size];
int split = 0;
up[1] = Math.min(value(record, c1, c2, c3, 0, 0), value(record, c1, c2, c3, 1, 1));
for (int i = 2; i < size; i++) {
int minsMax = towSubMatrixMin(record, c1, c2, c3, 0, split, i);
while (split < i) {
if (split == i - 1) {
break;
}
int moved = towSubMatrixMin(record, c1, c2, c3, 0, split + 1, i);
if (moved < minsMax) {
break;
} else {
minsMax = moved;
split++;
}
}
up[i] = minsMax;
}
return up;
}
public static int[] getDownSplitArray(int[][] record, int c1, int c2, int c3) {
int size = record.length;
int[] down = new int[size];
int split = size - 1;
down[size - 2] = Math.min(value(record, c1, c2, c3, size - 2, size - 2),
value(record, c1, c2, c3, size - 1, size - 1));
for (int i = size - 3; i >= 0; i--) {
int minsMax = towSubMatrixMin(record, c1, c2, c3, i, split - 1, size - 1);
while (split > i) {
if (split == i + 1) {
break;
}
int moved = towSubMatrixMin(record, c1, c2, c3, i, split - 2, size - 1);
if (moved < minsMax) {
break;
} else {
minsMax = moved;
split--;
}
}
down[i] = minsMax;
}
return down;
}
public static int towSubMatrixMin(int[][] record, int c1, int c2, int c3, int i, int split, int j) {
return Math.min(value(record, c1, c2, c3, i, split), value(record, c1, c2, c3, split + 1, j));
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
while (in.hasNext()) {
int n = in.nextInt();
int m = in.nextInt();
int[][] matrix = new int[n][m];
for (int i = 0; i < n; i++) {
char[] chas = in.next().toCharArray();
for (int j = 0; j < m; j++) {
matrix[i][j] = chas[j] - '0';
}
}
System.out.println(maxMinSumIn16(matrix));
}
in.close();
}
}

@ -0,0 +1,124 @@
package class41;
public class Code02_SplitArrayLargestSum {
public static int splitArray1(int[] nums, int m) {
return process(nums, 0, m);
}
public static int process(int[] arr, int index, int part) {
if (index == arr.length) {
return 0;
}
if (part == 0) {
return -1;
}
int first = 0;
int min = Integer.MAX_VALUE;
for (int end = index; arr.length - end >= part; end++) {
first += arr[end];
int next = process(arr, end + 1, part - 1);
if (next != -1) {
min = Math.min(min, Math.max(first, next));
}
}
return min;
}
public static int splitArray2(int[] nums, int M) {
int N = nums.length;
int[] help = new int[nums.length + 1];
for (int i = 0; i < N; i++) {
help[i + 1] = help[i] + nums[i];
}
int[][] dp = new int[N][M + 1];
for (int i = 0; i < N; i++) {
dp[i][1] = help[i + 1] - help[0];
}
for (int i = 1; i < Math.min(N, M); i++) {
dp[i][i + 1] = Math.max(dp[i - 1][i], nums[i]);
}
for (int i = 2; i < N; i++) {
for (int j = 2; j <= Math.min(i, M); j++) {
dp[i][j] = Integer.MAX_VALUE;
for (int k = i; k >= j - 1; k--) {
dp[i][j] = Math.min(dp[i][j], Math.max(dp[k - 1][j - 1], help[i + 1] - help[k]));
}
}
}
return dp[N - 1][M];
}
public static int splitArray3(int[] nums, int M) {
int N = nums.length;
int[] help = new int[nums.length + 1];
for (int i = 0; i < N; i++) {
help[i + 1] = help[i] + nums[i];
}
int[][] dp = new int[N][M + 1];
int[][] best = new int[N][M + 1];
for (int i = 0; i < N; i++) {
dp[i][1] = help[i + 1] - help[0];
}
for (int i = 1; i < Math.min(N, M); i++) {
dp[i][i + 1] = Math.max(dp[i - 1][i], nums[i]);
best[i][i + 1] = i;
}
for (int i = 2; i < N; i++) {
for (int j = Math.min(i, M); j >= 2; j--) {
dp[i][j] = Integer.MAX_VALUE;
int left = best[i - 1][j];
int right = j + 1 > M ? i : best[i][j + 1];
for (int k = left; k <= right; k++) {
int curAns = Math.max(dp[k - 1][j - 1], help[i + 1] - help[k]);
if (dp[i][j] > curAns) {
dp[i][j] = curAns;
best[i][j] = k;
}
}
}
}
return dp[N - 1][M];
}
public static int splitArray4(int[] nums, int M) {
long sum = 0;
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
}
long l = 0;
long r = sum;
long ans = 0;
while (l <= r) {
long mid = (l + r) / 2;
long cur = getNeedParts(nums, mid);
if (cur <= M) {
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
return (int) ans;
}
public static int getNeedParts(int[] arr, long aim) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] > aim) {
return Integer.MAX_VALUE;
}
}
int parts = 1;
int all = arr[0];
for (int i = 1; i < arr.length; i++) {
if (all + arr[i] > aim) {
parts++;
all = arr[i];
} else {
all += arr[i];
}
}
return parts;
}
}

@ -0,0 +1,194 @@
package class42;
public class Code01_ThrowChessPiecesProblem {
public static int solution1(int nLevel, int kChess) {
if (nLevel < 1 || kChess < 1) {
return 0;
}
return Process1(nLevel, kChess);
}
// rest还剩多少层楼需要去验证
// k还有多少颗棋子能够使用
// 一定要验证出最高的不会碎的楼层!但是每次都是坏运气。
// 返回至少需要扔几次?
public static int Process1(int rest, int k) {
if (rest == 0) {
return 0;
}
if (k == 1) {
return rest;
}
int min = Integer.MAX_VALUE;
for (int i = 1; i != rest + 1; i++) { // 第一次扔的时候仍在了i层
min = Math.min(min,
Math.max(Process1(i - 1, k - 1), Process1(rest - i, k)));
}
return min + 1;
}
public static int solution2(int nLevel, int kChess) {
if (nLevel < 1 || kChess < 1) {
return 0;
}
if (kChess == 1) {
return nLevel;
}
int[][] dp = new int[nLevel + 1][kChess + 1];
for (int i = 1; i != dp.length; i++) {
dp[i][1] = i;
}
for (int i = 1; i != dp.length; i++) {
for (int j = 2; j != dp[0].length; j++) {
int min = Integer.MAX_VALUE;
for (int k = 1; k != i + 1; k++) {
min = Math.min(min, Math.max(dp[k - 1][j - 1], dp[i - k][j]));
}
dp[i][j] = min + 1;
}
}
return dp[nLevel][kChess];
}
public static int solution3(int nLevel, int kChess) {
if (nLevel < 1 || kChess < 1) {
return 0;
}
if (kChess == 1) {
return nLevel;
}
int[] preArr = new int[nLevel + 1];
int[] curArr = new int[nLevel + 1];
for (int i = 1; i != curArr.length; i++) {
curArr[i] = i;
}
for (int i = 1; i != kChess; i++) {
int[] tmp = preArr;
preArr = curArr;
curArr = tmp;
for (int j = 1; j != curArr.length; j++) {
int min = Integer.MAX_VALUE;
for (int k = 1; k != j + 1; k++) {
min = Math.min(min, Math.max(preArr[k - 1], curArr[j - k]));
}
curArr[j] = min + 1;
}
}
return curArr[curArr.length - 1];
}
public static int solution4(int nLevel, int kChess) {
if (nLevel < 1 || kChess < 1) {
return 0;
}
if (kChess == 1) {
return nLevel;
}
int[][] dp = new int[nLevel + 1][kChess + 1];
for (int i = 1; i != dp.length; i++) {
dp[i][1] = i;
}
int[] cands = new int[kChess + 1];
for (int i = 1; i != dp[0].length; i++) {
dp[1][i] = 1;
cands[i] = 1;
}
for (int i = 2; i < nLevel + 1; i++) {
for (int j = kChess; j > 1; j--) {
int min = Integer.MAX_VALUE;
int minEnum = cands[j];
int maxEnum = j == kChess ? i / 2 + 1 : cands[j + 1];
for (int k = minEnum; k < maxEnum + 1; k++) {
int cur = Math.max(dp[k - 1][j - 1], dp[i - k][j]);
if (cur <= min) {
min = cur;
cands[j] = k;
}
}
dp[i][j] = min + 1;
}
}
return dp[nLevel][kChess];
}
public static int solution5(int nLevel, int kChess) {
if (nLevel < 1 || kChess < 1) {
return 0;
}
int bsTimes = log2N(nLevel) + 1;
if (kChess >= bsTimes) {
return bsTimes;
}
int[] dp = new int[kChess];
int res = 0;
while (true) {
res++;
int previous = 0;
for (int i = 0; i < dp.length; i++) {
int tmp = dp[i];
dp[i] = dp[i] + previous + 1;
previous = tmp;
if (dp[i] >= nLevel) {
return res;
}
}
}
}
public static int log2N(int n) {
int res = -1;
while (n != 0) {
res++;
n >>>= 1;
}
return res;
}
public static void main(String[] args) {
System.out.println(solution1(21, 2));
System.out.println(solution2(21, 2));
System.out.println(solution3(21, 2));
System.out.println(solution4(21, 2));
System.out.println(solution5(21, 2));
System.out.println("==============");
System.out.println(solution2(105, 2));
System.out.println(solution3(105, 2));
System.out.println(solution4(105, 2));
System.out.println(solution5(105, 2));
System.out.println("==============");
System.out.println(solution2(3000, 10));
System.out.println(solution3(3000, 10));
System.out.println(solution4(3000, 10));
System.out.println(solution5(3000, 10));
System.out.println("==============");
System.out.println(solution2(6884, 5));
System.out.println(solution3(6884, 5));
System.out.println(solution4(6884, 5));
System.out.println(solution5(6884, 5));
System.out.println("==============");
System.out.println(solution2(6885, 5));
System.out.println(solution3(6885, 5));
System.out.println(solution4(6885, 5));
System.out.println(solution5(6885, 5));
System.out.println("==============");
int nLevel = 100000000;
int kChess = 10;
long start = System.currentTimeMillis();
System.out.println(solution5(nLevel, kChess));
long end = System.currentTimeMillis();
System.out.println("cost time: " + (end - start) + " ms");
}
}

@ -0,0 +1,215 @@
package class42;
import java.util.Arrays;
public class Code02_PostOfficeProblem {
public static int minDis1(int[] arr, int num) {
if (arr == null || arr.length < 2 || num < 1) {
return 0;
}
// record[L][R]表示如果arr[L...R]上只建立一个邮局,总距离最小是多少?
int[][] record = getRecord(arr);
int N = arr.length;
int[][] dp = new int[N][num + 1];
// dp[...][0] 0个邮局的时候如何如何
// dp[0][...] 0
for (int i = 0; i < N; i++) {
// 0...i 1
dp[i][1] = record[0][i];
}
for (int i = 1; i < N; i++) {
for (int j = 2; j <= Math.min(num, i); j++) {
// dp[i][j]
// 枚举最后一个邮局负责的范围K...i
// i..i
// i-1..i
// i-2..i
// 1....i
// 0....i 单列
// 0...k-1 j-1个邮局 +
dp[i][j] = record[0][i];
for (int k = i; k > 0; k--) { // 1 .... i
dp[i][j] = Math.min(dp[i][j], dp[k - 1][j - 1] + record[k][i]);
}
}
}
return dp[N - 1][num];
}
public static int minDis2(int[] arr, int num) {
if (arr == null || arr.length < 2 || num < 1) {
return 0;
}
// record[L][R]表示如果arr[L...R]上只建立一个邮局,总距离最小是多少?
int[][] record = getRecord(arr);
int N = arr.length;
int[][] dp = new int[N][num + 1];
// dp[...][0]
// dp[0][...] 0..0 0
// choose[0][..] 0
// choose[i][j] 当时在求dp[i][j]的时候,
// 最右的邮局如果是在负责k...i这一段的时候取得的最优解请把choose[i][j] = k
int[][] choose = new int[N][num + 1];
for (int i = 0; i < N; i++) {
// 0..i 1个邮局 0...i 0
dp[i][1] = record[0][i];
}
for (int i = 1; i < N; i++) {
for (int j = Math.min(num, i); j >= 2; j--) {
int down = choose[i - 1][j];
int up = j == Math.min(num, i) ? i : choose[i][j + 1];
dp[i][j] = record[0][i];
for (int k = Math.max(1, down); k <= Math.min(up, i); k++) {
if (dp[k - 1][j - 1] + record[k][i] < dp[i][j]) {
dp[i][j] = dp[k - 1][j - 1] + record[k][i];
choose[i][j] = k;
}
}
}
}
return dp[N - 1][num];
}
public static int[][] getRecord(int[] arr) {
int N = arr.length;
int[][] record = new int[N][N];
for (int L = 0; L < N; L++) {
for (int R = L + 1; R < N; R++) {
record[L][R] = record[L][R - 1] + arr[R] - arr[(L + R) >> 1];
}
}
return record;
}
public static int minDistances1(int[] arr, int num) {
if (arr == null || num < 1 || arr.length < num) {
return 0;
}
int[][] w = new int[arr.length + 1][arr.length + 1];
for (int i = 0; i < arr.length; i++) {
for (int j = i + 1; j < arr.length; j++) {
w[i][j] = w[i][j - 1] + arr[j] - arr[(i + j) / 2];
}
}
int[][] dp = new int[num][arr.length];
for (int j = 0; j != arr.length; j++) {
dp[0][j] = w[0][j];
}
for (int i = 1; i < num; i++) {
for (int j = i + 1; j < arr.length; j++) {
dp[i][j] = Integer.MAX_VALUE;
for (int k = 0; k <= j; k++) {
dp[i][j] = Math.min(dp[i][j], dp[i - 1][k] + w[k + 1][j]);
}
}
}
return dp[num - 1][arr.length - 1];
}
public static int minDistances2(int[] arr, int num) {
if (arr == null || num < 1 || arr.length < num) {
return 0;
}
int[][] w = new int[arr.length + 1][arr.length + 1];
for (int i = 0; i < arr.length; i++) {
for (int j = i + 1; j < arr.length; j++) {
w[i][j] = w[i][j - 1] + arr[j] - arr[(i + j) / 2];
}
}
int[][] dp = new int[num][arr.length];
int[][] s = new int[num][arr.length];
for (int j = 0; j != arr.length; j++) {
dp[0][j] = w[0][j];
s[0][j] = 0;
}
int minK = 0;
int maxK = 0;
int cur = 0;
for (int i = 1; i < num; i++) {
for (int j = arr.length - 1; j > i; j--) {
minK = s[i - 1][j];
maxK = j == arr.length - 1 ? arr.length - 1 : s[i][j + 1];
dp[i][j] = Integer.MAX_VALUE;
for (int k = minK; k <= maxK; k++) {
cur = dp[i - 1][k] + w[k + 1][j];
if (cur <= dp[i][j]) {
dp[i][j] = cur;
s[i][j] = k;
}
}
}
}
return dp[num - 1][arr.length - 1];
}
// for test
public static int[] getSortedArray(int len, int range) {
int[] arr = new int[len];
for (int i = 0; i != len; i++) {
arr[i] = (int) (Math.random() * range);
}
Arrays.sort(arr);
return arr;
}
// for test
public static void printArray(int[] arr) {
for (int i = 0; i != arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
// for test
public static void main(String[] args) {
int[] arr = { 1,3,8,10,12 };
int num = 3;
System.out.println(minDis1(arr, num));
System.out.println(minDistances1(arr, num));
System.out.println(minDistances2(arr, num));
int times = 100; // test time
int len = 1000; // test array length
int range = 2000; // every number in [0,range)
int p = 50; // post office number max
long time1 = 0; // method1 all run time
long time2 = 0;// method2 all run time
long time3 = 0;
long start = 0;
long end = 0;
int res1 = 0;
int res2 = 0;
int res3 = 0;
for (int i = 0; i != times; i++) {
int office = (int) (Math.random() * p) + 1;
arr = getSortedArray(len, range);
start = System.currentTimeMillis();
res1 = minDistances1(arr, office);
end = System.currentTimeMillis();
time1 += end - start;
start = System.currentTimeMillis();
res2 = minDis2(arr, office);
end = System.currentTimeMillis();
time2 += end - start;
start = System.currentTimeMillis();
res3 = minDis1(arr, office);
end = System.currentTimeMillis();
time3 += end - start;
if (res1 != res2 || res1 != res3) {
printArray(arr);
break;
}
if (i % 10 == 0) {
System.out.print(". ");
}
}
System.out.println();
System.out.println("method1 all run time(ms): " + time1);
System.out.println("method2 all run time(ms): " + time2);
System.out.println("method3 all run time(ms): " + time3);
}
}

@ -0,0 +1,295 @@
package class43;
import java.util.ArrayList;
import java.util.List;
public class Code01_TSP {
public static int t1(int[][] matrix) {
int N = matrix.length; // 0...N-1
// set
// set.get(i) != null i这座城市在集合里
// set.get(i) == null i这座城市不在集合里
List<Integer> set = new ArrayList<>();
for (int i = 0; i < N; i++) {
set.add(1);
}
return func1(matrix, set, 0);
}
// 任何两座城市之间的距离可以在matrix里面拿到
// set中表示着哪些城市的集合
// start这座城一定在set里
// 从start出发要把set中所有的城市过一遍最终回到0这座城市最小距离是多少
public static int func1(int[][] matrix, List<Integer> set, int start) {
int cityNum = 0;
for (int i = 0; i < set.size(); i++) {
if (set.get(i) != null) {
cityNum++;
}
}
if (cityNum == 1) {
return matrix[start][0];
}
// 不只start这一座城
set.set(start, null);
int min = Integer.MAX_VALUE;
for (int i = 0; i < set.size(); i++) {
if (set.get(i) != null && i != start) {
// start -> i i... -> 0
int cur = matrix[start][i] + func1(matrix, set, i);
min = Math.min(min, cur);
}
}
set.set(start, 1);
return min;
}
public static int t2(int[][] matrix) {
int N = matrix.length; // 0...N-1
// 7座城 1111111
int allCity = (1 << N) - 1;
return f2(matrix, allCity, 0);
}
// 任何两座城市之间的距离可以在matrix里面拿到
// set中表示着哪些城市的集合
// start这座城一定在set里
// 从start出发要把set中所有的城市过一遍最终回到0这座城市最小距离是多少
public static int f2(int[][] matrix, int cityStatus, int start) {
// cityStatus == cityStatux & (~cityStaus + 1)
if (cityStatus == (cityStatus & (~cityStatus + 1))) {
return matrix[start][0];
}
// 把start位的1去掉
cityStatus &= (~(1 << start));
int min = Integer.MAX_VALUE;
// 枚举所有的城市
for (int move = 0; move < matrix.length; move++) {
if (move != start && (cityStatus & (1 << move)) != 0) {
int cur = matrix[start][move] + f2(matrix, cityStatus, move);
min = Math.min(min, cur);
}
}
cityStatus |= (1 << start);
return min;
}
public static int t3(int[][] matrix) {
int N = matrix.length; // 0...N-1
// 7座城 1111111
int allCity = (1 << N) - 1;
int[][] dp = new int[1 << N][N];
for (int i = 0; i < (1 << N); i++) {
for (int j = 0; j < N; j++) {
dp[i][j] = -1;
}
}
return f3(matrix, allCity, 0, dp);
}
// 任何两座城市之间的距离可以在matrix里面拿到
// set中表示着哪些城市的集合
// start这座城一定在set里
// 从start出发要把set中所有的城市过一遍最终回到0这座城市最小距离是多少
public static int f3(int[][] matrix, int cityStatus, int start, int[][] dp) {
if (dp[cityStatus][start] != -1) {
return dp[cityStatus][start];
}
if (cityStatus == (cityStatus & (~cityStatus + 1))) {
dp[cityStatus][start] = matrix[start][0];
} else {
// 把start位的1去掉
cityStatus &= (~(1 << start));
int min = Integer.MAX_VALUE;
// 枚举所有的城市
for (int move = 0; move < matrix.length; move++) {
if (move != start && (cityStatus & (1 << move)) != 0) {
int cur = matrix[start][move] + f3(matrix, cityStatus, move, dp);
min = Math.min(min, cur);
}
}
cityStatus |= (1 << start);
dp[cityStatus][start] = min;
}
return dp[cityStatus][start];
}
public static int t4(int[][] matrix) {
int N = matrix.length; // 0...N-1
int statusNums = 1 << N;
int[][] dp = new int[statusNums][N];
for (int status = 0; status < statusNums; status++) {
for (int start = 0; start < N; start++) {
if ((status & (1 << start)) != 0) {
if (status == (status & (~status + 1))) {
dp[status][start] = matrix[start][0];
} else {
int min = Integer.MAX_VALUE;
// start 城市在status里去掉之后的状态
int preStatus = status & (~(1 << start));
// start -> i
for (int i = 0; i < N; i++) {
if ((preStatus & (1 << i)) != 0) {
int cur = matrix[start][i] + dp[preStatus][i];
min = Math.min(min, cur);
}
}
dp[status][start] = min;
}
}
}
}
return dp[statusNums - 1][0];
}
// matrix[i][j] -> i城市到j城市的距离
public static int tsp1(int[][] matrix, int origin) {
if (matrix == null || matrix.length < 2 || origin < 0 || origin >= matrix.length) {
return 0;
}
// 要考虑的集合
ArrayList<Integer> cities = new ArrayList<>();
// cities[0] != null 表示0城在集合里
// cities[i] != null 表示i城在集合里
for (int i = 0; i < matrix.length; i++) {
cities.add(1);
}
// null,1,1,1,1,1,1
// origin城不参与集合
cities.set(origin, null);
return process(matrix, origin, cities, origin);
}
// matrix 所有距离,存在其中
// origin 固定参数,唯一的目标
// cities 要考虑的集合一定不含有origin
// 当前来到的城市是谁cur
public static int process(int[][] matrix, int aim, ArrayList<Integer> cities, int cur) {
boolean hasCity = false; // 集团中还是否有城市
int ans = Integer.MAX_VALUE;
for (int i = 0; i < cities.size(); i++) {
if (cities.get(i) != null) {
hasCity = true;
cities.set(i, null);
// matrix[cur][i] + f(i, 集团(去掉i) )
ans = Math.min(ans, matrix[cur][i] + process(matrix, aim, cities, i));
cities.set(i, 1);
}
}
return hasCity ? ans : matrix[cur][aim];
}
// cities 里一定含有cur这座城
// 解决的是集合从cur出发通过集合里所有的城市最终来到aim最短距离
public static int process2(int[][] matrix, int aim, ArrayList<Integer> cities, int cur) {
if (cities.size() == 1) {
return matrix[cur][aim];
}
cities.set(cur, null);
int ans = Integer.MAX_VALUE;
for (int i = 0; i < cities.size(); i++) {
if (cities.get(i) != null) {
int dis = matrix[cur][i] + process2(matrix, aim, cities, i);
ans = Math.min(ans, dis);
}
}
cities.set(cur, 1);
return ans;
}
public static int tsp2(int[][] matrix, int origin) {
if (matrix == null || matrix.length < 2 || origin < 0 || origin >= matrix.length) {
return 0;
}
int N = matrix.length - 1; // 除去origin之后是n-1个点
int S = 1 << N; // 状态数量
int[][] dp = new int[S][N];
int icity = 0;
int kcity = 0;
for (int i = 0; i < N; i++) {
icity = i < origin ? i : i + 1;
// 00000000 i
dp[0][i] = matrix[icity][origin];
}
for (int status = 1; status < S; status++) {
// 尝试每一种状态 status = 0 0 1 0 0 0 0 0 0
// 下标 8 7 6 5 4 3 2 1 0
for (int i = 0; i < N; i++) {
// i 枚举的出发城市
dp[status][i] = Integer.MAX_VALUE;
if ((1 << i & status) != 0) {
// 如果i这座城是可以枚举的i = 6 i对应的原始城的编号icity
icity = i < origin ? i : i + 1;
for (int k = 0; k < N; k++) { // i 这一步连到的点k
if ((1 << k & status) != 0) { // i 这一步可以连到k
kcity = k < origin ? k : k + 1; // k对应的原始城的编号kcity
dp[status][i] = Math.min(dp[status][i], dp[status ^ (1 << i)][k] + matrix[icity][kcity]);
}
}
}
}
}
int ans = Integer.MAX_VALUE;
for (int i = 0; i < N; i++) {
icity = i < origin ? i : i + 1;
ans = Math.min(ans, dp[S - 1][i] + matrix[origin][icity]);
}
return ans;
}
public static int[][] generateGraph(int maxSize, int maxValue) {
int len = (int) (Math.random() * maxSize) + 1;
int[][] matrix = new int[len][len];
for (int i = 0; i < len; i++) {
for (int j = 0; j < len; j++) {
matrix[i][j] = (int) (Math.random() * maxValue) + 1;
}
}
for (int i = 0; i < len; i++) {
matrix[i][i] = 0;
}
return matrix;
}
public static void main(String[] args) {
int len = 10;
int value = 100;
System.out.println("功能测试开始");
for (int i = 0; i < 20000; i++) {
int[][] matrix = generateGraph(len, value);
int origin = (int) (Math.random() * matrix.length);
int ans1 = t3(matrix);
int ans2 = t4(matrix);
int ans3 = tsp2(matrix, origin);
if (ans1 != ans2 || ans1 != ans3) {
System.out.println("fuck");
}
}
System.out.println("功能测试结束");
len = 22;
System.out.println("性能测试开始,数据规模 : " + len);
int[][] matrix = new int[len][len];
for (int i = 0; i < len; i++) {
for (int j = 0; j < len; j++) {
matrix[i][j] = (int) (Math.random() * value) + 1;
}
}
for (int i = 0; i < len; i++) {
matrix[i][i] = 0;
}
long start;
long end;
start = System.currentTimeMillis();
t4(matrix);
end = System.currentTimeMillis();
System.out.println("运行时间 : " + (end - start) + " 毫秒");
System.out.println("性能测试结束");
}
}

@ -0,0 +1,131 @@
package class43;
public class Code02_PavingTile {
/*
* 2*MN*M
*/
public static int ways1(int N, int M) {
if (N < 1 || M < 1 || ((N * M) & 1) != 0) {
return 0;
}
if (N == 1 || M == 1) {
return 1;
}
int[] pre = new int[M]; // pre代表-1行的状况
for (int i = 0; i < pre.length; i++) {
pre[i] = 1;
}
return process(pre, 0, N);
}
// pre 表示level-1行的状态
// level表示正在level行做决定
// N 表示一共有多少行 固定的
// level-2行及其之上所有行都摆满砖了
// level做决定让所有区域都满方法数返回s
public static int process(int[] pre, int level, int N) {
if (level == N) { // base case
for (int i = 0; i < pre.length; i++) {
if (pre[i] == 0) {
return 0;
}
}
return 1;
}
// 没到终止行可以选择在当前的level行摆瓷砖
int[] op = getOp(pre);
return dfs(op, 0, level, N);
}
// op[i] == 0 可以考虑摆砖
// op[i] == 1 只能竖着向上
public static int dfs(int[] op, int col, int level, int N) {
// 在列上自由发挥玩深度优先遍历当col来到终止列i行的决定做完了
// 轮到i+1行做决定
if (col == op.length) {
return process(op, level + 1, N);
}
int ans = 0;
// col位置不横摆
ans += dfs(op, col + 1, level, N); // col位置上不摆横转
// col位置横摆, 向右
if (col + 1 < op.length && op[col] == 0 && op[col + 1] == 0) {
op[col] = 1;
op[col + 1] = 1;
ans += dfs(op, col + 2, level, N);
op[col] = 0;
op[col + 1] = 0;
}
return ans;
}
public static int[] getOp(int[] pre) {
int[] cur = new int[pre.length];
for (int i = 0; i < pre.length; i++) {
cur[i] = pre[i] ^ 1;
}
return cur;
}
public static int ways2(int N, int M) {
if (N < 1 || M < 1 || ((N * M) & 1) != 0) {
return 0;
}
if (N == 1 || M == 1) {
return 1;
}
int big = N > M ? N : M;
int small = big == N ? M : N;
// big * small
int sn = 1 << small;
int limit = sn - 1; // 全满状态
int[] dp = new int[sn];
dp[limit] = 1; // -1行全是满的
// dp -1 行的状况
// dp[0000] 0
// dp[0001] 0
// dp[1111] 1
int[] cur = new int[sn]; // 当前行, 要算出所有状态下的解
for (int level = 0; level < big; level++) {
for (int status = 0; status < sn; status++) {
if (dp[status] != 0) { // 状态出现了
// 0...1100
// 1...0011
// 0...1111
// 0...0011
int op = (~status) & limit;
dfs(dp[status], op, 0, small - 1, cur);
}
}
for (int i = 0; i < sn; i++) {
dp[i] = 0;
}
int[] tmp = dp;
dp = cur;
cur = tmp;
}
return dp[limit];
}
public static void dfs(int way, int op, int index, int end, int[] cur) {
if (index == end) {
cur[op] += way;
} else {
dfs(way, op, index + 1, end, cur);
if (((3 << index) & op) == 0) { // 11 << index 可以放砖
dfs(way, op | (3 << index), index + 1, end, cur);
}
}
}
public static void main(String[] args) {
int N = 8;
int M = 8;
System.out.println(ways1(N, M));
System.out.println(ways2(N, M));
}
}

@ -0,0 +1,64 @@
package class43;
// leetcode 464题
public class Code03_CanIWin {
// 这个是暴力尝试,思路是正确的,超时而已
public static boolean canIWin1(int choose, int total) {
if (total == 0) {
return true;
}
if ((choose * (choose + 1) >> 1) < total) {
return false;
}
return process1(choose, 0, total);
}
public static boolean process1(int choose, int status, int rest) {
if (rest <= 0) {
return false;
}
for (int i = 1; i <= choose; i++) {
if (((1 << i) & status) == 0) {
if (!process1(choose, (status | (1 << i)), rest - i)) {
return true;
}
}
}
return false;
}
// 暴力尝试改动态规划而已
public static boolean canIWin2(int choose, int total) {
if (total == 0) {
return true;
}
if ((choose * (choose + 1) >> 1) < total) {
return false;
}
int[] dp = new int[1 << (choose + 1)];
return process2(choose, 0, total, dp);
}
// 为什么明明status和rest是两个可变参数却只用status来代表状态(也就是dp)
// 因为选了一批数字之后得到的和一定是一样的所以rest是由status决定的所以rest不需要参与记忆化搜索
public static boolean process2(int choose, int status, int rest, int[] dp) {
if (dp[status] != 0) {
return dp[status] == 1 ? true : false;
}
boolean ans = false;
if (rest > 0) {
for (int i = 1; i <= choose; i++) {
if (((1 << i) & status) == 0) {
if (!process2(choose, (status | (1 << i)), rest - i, dp)) {
ans = true;
break;
}
}
}
}
dp[status] = ans ? 1 : -1;
return ans;
}
}

@ -0,0 +1,248 @@
package class44;
// 测试链接: https://leetcode.com/problems/create-maximum-number/
public class Code01_CreateMaximumNumber {
public static int[] maxNumber1(int[] nums1, int[] nums2, int k) {
int len1 = nums1.length;
int len2 = nums2.length;
if (k < 0 || k > len1 + len2) {
return null;
}
int[] res = new int[k];
int[][] dp1 = getdp(nums1); // 生成dp1这个表以后从nums1中只要固定拿N个数
int[][] dp2 = getdp(nums2);
for (int get1 = Math.max(0, k - len2); get1 <= Math.min(k, len1); get1++) {
// arr1 挑 get1个怎么得到一个最优结果
int[] pick1 = maxPick(nums1, dp1, get1);
int[] pick2 = maxPick(nums2, dp2, k - get1);
int[] merge = merge(pick1, pick2);
res = preMoreThanLast(res, 0, merge, 0) ? res : merge;
}
return res;
}
public static int[] merge(int[] nums1, int[] nums2) {
int k = nums1.length + nums2.length;
int[] ans = new int[k];
for (int i = 0, j = 0, r = 0; r < k; ++r) {
ans[r] = preMoreThanLast(nums1, i, nums2, j) ? nums1[i++] : nums2[j++];
}
return ans;
}
public static boolean preMoreThanLast(int[] nums1, int i, int[] nums2, int j) {
while (i < nums1.length && j < nums2.length && nums1[i] == nums2[j]) {
i++;
j++;
}
return j == nums2.length || (i < nums1.length && nums1[i] > nums2[j]);
}
public static int[] maxNumber2(int[] nums1, int[] nums2, int k) {
int len1 = nums1.length;
int len2 = nums2.length;
if (k < 0 || k > len1 + len2) {
return null;
}
int[] res = new int[k];
int[][] dp1 = getdp(nums1);
int[][] dp2 = getdp(nums2);
for (int get1 = Math.max(0, k - len2); get1 <= Math.min(k, len1); get1++) {
int[] pick1 = maxPick(nums1, dp1, get1);
int[] pick2 = maxPick(nums2, dp2, k - get1);
int[] merge = mergeBySuffixArray(pick1, pick2);
res = moreThan(res, merge) ? res : merge;
}
return res;
}
public static boolean moreThan(int[] pre, int[] last) {
int i = 0;
int j = 0;
while (i < pre.length && j < last.length && pre[i] == last[j]) {
i++;
j++;
}
return j == last.length || (i < pre.length && pre[i] > last[j]);
}
public static int[] mergeBySuffixArray(int[] nums1, int[] nums2) {
int size1 = nums1.length;
int size2 = nums2.length;
int[] nums = new int[size1 + 1 + size2];
for (int i = 0; i < size1; i++) {
nums[i] = nums1[i] + 2;
}
nums[size1] = 1;
for (int j = 0; j < size2; j++) {
nums[j + size1 + 1] = nums2[j] + 2;
}
DC3 dc3 = new DC3(nums, 11);
int[] rank = dc3.rank;
int[] ans = new int[size1 + size2];
int i = 0;
int j = 0;
int r = 0;
while (i < size1 && j < size2) {
ans[r++] = rank[i] > rank[j + size1 + 1] ? nums1[i++] : nums2[j++];
}
while (i < size1) {
ans[r++] = nums1[i++];
}
while (j < size2) {
ans[r++] = nums2[j++];
}
return ans;
}
public static class DC3 {
public int[] sa;
public int[] rank;
public DC3(int[] nums, int max) {
sa = sa(nums, max);
rank = rank();
}
private int[] sa(int[] nums, int max) {
int n = nums.length;
int[] arr = new int[n + 3];
for (int i = 0; i < n; i++) {
arr[i] = nums[i];
}
return skew(arr, n, max);
}
private int[] skew(int[] nums, int n, int K) {
int n0 = (n + 2) / 3, n1 = (n + 1) / 3, n2 = n / 3, n02 = n0 + n2;
int[] s12 = new int[n02 + 3], sa12 = new int[n02 + 3];
for (int i = 0, j = 0; i < n + (n0 - n1); ++i) {
if (0 != i % 3) {
s12[j++] = i;
}
}
radixPass(nums, s12, sa12, 2, n02, K);
radixPass(nums, sa12, s12, 1, n02, K);
radixPass(nums, s12, sa12, 0, n02, K);
int name = 0, c0 = -1, c1 = -1, c2 = -1;
for (int i = 0; i < n02; ++i) {
if (c0 != nums[sa12[i]] || c1 != nums[sa12[i] + 1] || c2 != nums[sa12[i] + 2]) {
name++;
c0 = nums[sa12[i]];
c1 = nums[sa12[i] + 1];
c2 = nums[sa12[i] + 2];
}
if (1 == sa12[i] % 3) {
s12[sa12[i] / 3] = name;
} else {
s12[sa12[i] / 3 + n0] = name;
}
}
if (name < n02) {
sa12 = skew(s12, n02, name);
for (int i = 0; i < n02; i++) {
s12[sa12[i]] = i + 1;
}
} else {
for (int i = 0; i < n02; i++) {
sa12[s12[i] - 1] = i;
}
}
int[] s0 = new int[n0], sa0 = new int[n0];
for (int i = 0, j = 0; i < n02; i++) {
if (sa12[i] < n0) {
s0[j++] = 3 * sa12[i];
}
}
radixPass(nums, s0, sa0, 0, n0, K);
int[] sa = new int[n];
for (int p = 0, t = n0 - n1, k = 0; k < n; k++) {
int i = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2;
int j = sa0[p];
if (sa12[t] < n0 ? leq(nums[i], s12[sa12[t] + n0], nums[j], s12[j / 3])
: leq(nums[i], nums[i + 1], s12[sa12[t] - n0 + 1], nums[j], nums[j + 1], s12[j / 3 + n0])) {
sa[k] = i;
t++;
if (t == n02) {
for (k++; p < n0; p++, k++) {
sa[k] = sa0[p];
}
}
} else {
sa[k] = j;
p++;
if (p == n0) {
for (k++; t < n02; t++, k++) {
sa[k] = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2;
}
}
}
}
return sa;
}
private void radixPass(int[] nums, int[] input, int[] output, int offset, int n, int k) {
int[] cnt = new int[k + 1];
for (int i = 0; i < n; ++i) {
cnt[nums[input[i] + offset]]++;
}
for (int i = 0, sum = 0; i < cnt.length; ++i) {
int t = cnt[i];
cnt[i] = sum;
sum += t;
}
for (int i = 0; i < n; ++i) {
output[cnt[nums[input[i] + offset]]++] = input[i];
}
}
private boolean leq(int a1, int a2, int b1, int b2) {
return a1 < b1 || (a1 == b1 && a2 <= b2);
}
private boolean leq(int a1, int a2, int a3, int b1, int b2, int b3) {
return a1 < b1 || (a1 == b1 && leq(a2, a3, b2, b3));
}
private int[] rank() {
int n = sa.length;
int[] ans = new int[n];
for (int i = 0; i < n; i++) {
ans[sa[i]] = i + 1;
}
return ans;
}
}
public static int[][] getdp(int[] arr) {
int size = arr.length; // 0~N-1
int pick = arr.length + 1; // 1 ~ N
int[][] dp = new int[size][pick];
// get 不从0开始因为拿0个无意义
// get 1
for (int get = 1; get < pick; get++) { // 1 ~ N
int maxIndex = size - get;
// i~N-1
for (int i = size - get; i >= 0; i--) {
if (arr[i] >= arr[maxIndex]) {
maxIndex = i;
}
dp[i][get] = maxIndex;
}
}
return dp;
}
public static int[] maxPick(int[] arr, int[][] dp, int pick) {
int[] res = new int[pick];
for (int resIndex = 0, dpRow = 0; pick > 0; pick--, resIndex++) {
res[resIndex] = arr[dp[dpRow][pick]];
dpRow = dp[dpRow][pick] + 1;
}
return res;
}
}

@ -0,0 +1,148 @@
package class44;
// 测试链接: https://leetcode.com/problems/last-substring-in-lexicographical-order/
public class Code02_LastSubstringInLexicographicalOrder {
public static String lastSubstring(String s) {
if (s == null || s.length() == 0) {
return s;
}
int N = s.length();
char[] str = s.toCharArray();
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for (char cha : str) {
min = Math.min(min, cha);
max = Math.max(max, cha);
}
int[] arr = new int[N];
for (int i = 0; i < N; i++) {
arr[i] = str[i] - min + 1;
}
DC3 dc3 = new DC3(arr, max - min + 1);
return s.substring(dc3.sa[N - 1]);
}
public static class DC3 {
public int[] sa;
public int[] rank;
public DC3(int[] nums, int max) {
sa = sa(nums, max);
rank = rank();
}
private int[] sa(int[] nums, int max) {
int n = nums.length;
int[] arr = new int[n + 3];
for (int i = 0; i < n; i++) {
arr[i] = nums[i];
}
return skew(arr, n, max);
}
private int[] skew(int[] nums, int n, int K) {
int n0 = (n + 2) / 3, n1 = (n + 1) / 3, n2 = n / 3, n02 = n0 + n2;
int[] s12 = new int[n02 + 3], sa12 = new int[n02 + 3];
for (int i = 0, j = 0; i < n + (n0 - n1); ++i) {
if (0 != i % 3) {
s12[j++] = i;
}
}
radixPass(nums, s12, sa12, 2, n02, K);
radixPass(nums, sa12, s12, 1, n02, K);
radixPass(nums, s12, sa12, 0, n02, K);
int name = 0, c0 = -1, c1 = -1, c2 = -1;
for (int i = 0; i < n02; ++i) {
if (c0 != nums[sa12[i]] || c1 != nums[sa12[i] + 1] || c2 != nums[sa12[i] + 2]) {
name++;
c0 = nums[sa12[i]];
c1 = nums[sa12[i] + 1];
c2 = nums[sa12[i] + 2];
}
if (1 == sa12[i] % 3) {
s12[sa12[i] / 3] = name;
} else {
s12[sa12[i] / 3 + n0] = name;
}
}
if (name < n02) {
sa12 = skew(s12, n02, name);
for (int i = 0; i < n02; i++) {
s12[sa12[i]] = i + 1;
}
} else {
for (int i = 0; i < n02; i++) {
sa12[s12[i] - 1] = i;
}
}
int[] s0 = new int[n0], sa0 = new int[n0];
for (int i = 0, j = 0; i < n02; i++) {
if (sa12[i] < n0) {
s0[j++] = 3 * sa12[i];
}
}
radixPass(nums, s0, sa0, 0, n0, K);
int[] sa = new int[n];
for (int p = 0, t = n0 - n1, k = 0; k < n; k++) {
int i = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2;
int j = sa0[p];
if (sa12[t] < n0 ? leq(nums[i], s12[sa12[t] + n0], nums[j], s12[j / 3])
: leq(nums[i], nums[i + 1], s12[sa12[t] - n0 + 1], nums[j], nums[j + 1], s12[j / 3 + n0])) {
sa[k] = i;
t++;
if (t == n02) {
for (k++; p < n0; p++, k++) {
sa[k] = sa0[p];
}
}
} else {
sa[k] = j;
p++;
if (p == n0) {
for (k++; t < n02; t++, k++) {
sa[k] = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2;
}
}
}
}
return sa;
}
private void radixPass(int[] nums, int[] input, int[] output, int offset, int n, int k) {
int[] cnt = new int[k + 1];
for (int i = 0; i < n; ++i) {
cnt[nums[input[i] + offset]]++;
}
for (int i = 0, sum = 0; i < cnt.length; ++i) {
int t = cnt[i];
cnt[i] = sum;
sum += t;
}
for (int i = 0; i < n; ++i) {
output[cnt[nums[input[i] + offset]]++] = input[i];
}
}
private boolean leq(int a1, int a2, int b1, int b2) {
return a1 < b1 || (a1 == b1 && a2 <= b2);
}
private boolean leq(int a1, int a2, int a3, int b1, int b2, int b3) {
return a1 < b1 || (a1 == b1 && leq(a2, a3, b2, b3));
}
private int[] rank() {
int n = sa.length;
int[] ans = new int[n];
for (int i = 0; i < n; i++) {
ans[sa[i]] = i + 1;
}
return ans;
}
}
}

@ -0,0 +1,249 @@
package class44;
public class Code03_InsertS2MakeMostAlphabeticalOrder {
// 暴力方法
public static String right(String s1, String s2) {
if (s1 == null || s1.length() == 0) {
return s2;
}
if (s2 == null || s2.length() == 0) {
return s1;
}
String p1 = s1 + s2;
String p2 = s2 + s1;
String ans = p1.compareTo(p2) > 0 ? p1 : p2;
for (int end = 1; end < s1.length(); end++) {
String cur = s1.substring(0, end) + s2 + s1.substring(end);
if (cur.compareTo(ans) > 0) {
ans = cur;
}
}
return ans;
}
// 正式方法
public static String max(String s1, String s2) {
if (s1 == null || s1.length() == 0) {
return s2;
}
if (s2 == null || s2.length() == 0) {
return s1;
}
char[] str1 = s1.toCharArray();
char[] str2 = s2.toCharArray();
int N = str1.length;
int M = str2.length;
int min = str1[0];
int max = str1[0];
for (int i = 1; i < N; i++) {
min = Math.min(min, str1[i]);
max = Math.max(max, str1[i]);
}
for (int i = 0; i < M; i++) {
min = Math.min(min, str2[i]);
max = Math.max(max, str2[i]);
}
int[] all = new int[N + M + 1];
int index = 0;
for (int i = 0; i < N; i++) {
all[index++] = str1[i] - min + 2;
}
all[index++] = 1;
for (int i = 0; i < M; i++) {
all[index++] = str2[i] - min + 2;
}
DC3 dc3 = new DC3(all, max - min + 2);
int[] rank = dc3.rank;
int comp = N + 1;
for (int i = 0; i < N; i++) {
if (rank[i] < rank[comp]) {
int best = whereSplit(s1, s2, i);
return s1.substring(0, best) + s2 + s1.substring(best);
}
}
return s1 + s2;
}
public static class DC3 {
public int[] sa;
public int[] rank;
public DC3(int[] nums, int max) {
sa = sa(nums, max);
rank = rank();
}
private int[] sa(int[] nums, int max) {
int n = nums.length;
int[] arr = new int[n + 3];
for (int i = 0; i < n; i++) {
arr[i] = nums[i];
}
return skew(arr, n, max);
}
private int[] skew(int[] nums, int n, int K) {
int n0 = (n + 2) / 3, n1 = (n + 1) / 3, n2 = n / 3, n02 = n0 + n2;
int[] s12 = new int[n02 + 3], sa12 = new int[n02 + 3];
for (int i = 0, j = 0; i < n + (n0 - n1); ++i) {
if (0 != i % 3) {
s12[j++] = i;
}
}
radixPass(nums, s12, sa12, 2, n02, K);
radixPass(nums, sa12, s12, 1, n02, K);
radixPass(nums, s12, sa12, 0, n02, K);
int name = 0, c0 = -1, c1 = -1, c2 = -1;
for (int i = 0; i < n02; ++i) {
if (c0 != nums[sa12[i]] || c1 != nums[sa12[i] + 1] || c2 != nums[sa12[i] + 2]) {
name++;
c0 = nums[sa12[i]];
c1 = nums[sa12[i] + 1];
c2 = nums[sa12[i] + 2];
}
if (1 == sa12[i] % 3) {
s12[sa12[i] / 3] = name;
} else {
s12[sa12[i] / 3 + n0] = name;
}
}
if (name < n02) {
sa12 = skew(s12, n02, name);
for (int i = 0; i < n02; i++) {
s12[sa12[i]] = i + 1;
}
} else {
for (int i = 0; i < n02; i++) {
sa12[s12[i] - 1] = i;
}
}
int[] s0 = new int[n0], sa0 = new int[n0];
for (int i = 0, j = 0; i < n02; i++) {
if (sa12[i] < n0) {
s0[j++] = 3 * sa12[i];
}
}
radixPass(nums, s0, sa0, 0, n0, K);
int[] sa = new int[n];
for (int p = 0, t = n0 - n1, k = 0; k < n; k++) {
int i = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2;
int j = sa0[p];
if (sa12[t] < n0 ? leq(nums[i], s12[sa12[t] + n0], nums[j], s12[j / 3])
: leq(nums[i], nums[i + 1], s12[sa12[t] - n0 + 1], nums[j], nums[j + 1], s12[j / 3 + n0])) {
sa[k] = i;
t++;
if (t == n02) {
for (k++; p < n0; p++, k++) {
sa[k] = sa0[p];
}
}
} else {
sa[k] = j;
p++;
if (p == n0) {
for (k++; t < n02; t++, k++) {
sa[k] = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2;
}
}
}
}
return sa;
}
private void radixPass(int[] nums, int[] input, int[] output, int offset, int n, int k) {
int[] cnt = new int[k + 1];
for (int i = 0; i < n; ++i) {
cnt[nums[input[i] + offset]]++;
}
for (int i = 0, sum = 0; i < cnt.length; ++i) {
int t = cnt[i];
cnt[i] = sum;
sum += t;
}
for (int i = 0; i < n; ++i) {
output[cnt[nums[input[i] + offset]]++] = input[i];
}
}
private boolean leq(int a1, int a2, int b1, int b2) {
return a1 < b1 || (a1 == b1 && a2 <= b2);
}
private boolean leq(int a1, int a2, int a3, int b1, int b2, int b3) {
return a1 < b1 || (a1 == b1 && leq(a2, a3, b2, b3));
}
private int[] rank() {
int n = sa.length;
int[] ans = new int[n];
for (int i = 0; i < n; i++) {
ans[sa[i]] = i + 1;
}
return ans;
}
}
public static int whereSplit(String str1, String str2, int first) {
int M = str2.length();
String bestPrefix = str2;
int bestSplit = first;
for (int i = first + 1, j = M - 1; i <= Math.min(str1.length(), first + M); i++, j--) {
String curPrefix = str1.substring(first, i) + str2.substring(0, j);
if (curPrefix.compareTo(bestPrefix) >= 0) {
bestPrefix = curPrefix;
bestSplit = i;
}
}
return bestSplit;
}
// for test
public static String randomNumberString(int len, int range) {
char[] str = new char[len];
for (int i = 0; i < len; i++) {
str[i] = (char) ((int) (Math.random() * range) + '0');
}
return String.valueOf(str);
}
// for test
public static void main(String[] args) {
int range = 10;
int len = 50;
int testTime = 100000;
System.out.println("功能测试开始");
for (int i = 0; i < testTime; i++) {
int s1Len = (int) (Math.random() * len);
int s2Len = (int) (Math.random() * len);
String s1 = randomNumberString(s1Len, range);
String s2 = randomNumberString(s2Len, range);
String ans1 = right(s1, s2);
String ans2 = max(s1, s2);
if (!ans1.equals(ans2)) {
System.out.println("Oops!");
System.out.println(s1);
System.out.println(s2);
System.out.println(ans1);
System.out.println(ans2);
break;
}
}
System.out.println("功能测试结束");
System.out.println("==========");
System.out.println("性能测试开始");
int s1Len = 1000000;
int s2Len = 50;
String s1 = randomNumberString(s1Len, range);
String s2 = randomNumberString(s2Len, range);
long start = System.currentTimeMillis();
max(s1, s2);
long end = System.currentTimeMillis();
System.out.println("运行时间 : " + (end - start) + " ms");
System.out.println("性能测试结束");
}
}

@ -0,0 +1,123 @@
package class44;
public class DC3 {
public int[] sa;
public int[] rank;
public DC3(int[] nums, int max) {
sa = sa(nums, max);
rank = rank();
}
private int[] sa(int[] nums, int max) {
int n = nums.length;
int[] arr = new int[n + 3];
for (int i = 0; i < n; i++) {
arr[i] = nums[i];
}
return skew(arr, n, max);
}
private int[] skew(int[] nums, int n, int K) {
int n0 = (n + 2) / 3, n1 = (n + 1) / 3, n2 = n / 3, n02 = n0 + n2;
int[] s12 = new int[n02 + 3], sa12 = new int[n02 + 3];
for (int i = 0, j = 0; i < n + (n0 - n1); ++i) {
if (0 != i % 3) {
s12[j++] = i;
}
}
radixPass(nums, s12, sa12, 2, n02, K);
radixPass(nums, sa12, s12, 1, n02, K);
radixPass(nums, s12, sa12, 0, n02, K);
int name = 0, c0 = -1, c1 = -1, c2 = -1;
for (int i = 0; i < n02; ++i) {
if (c0 != nums[sa12[i]] || c1 != nums[sa12[i] + 1] || c2 != nums[sa12[i] + 2]) {
name++;
c0 = nums[sa12[i]];
c1 = nums[sa12[i] + 1];
c2 = nums[sa12[i] + 2];
}
if (1 == sa12[i] % 3) {
s12[sa12[i] / 3] = name;
} else {
s12[sa12[i] / 3 + n0] = name;
}
}
if (name < n02) {
sa12 = skew(s12, n02, name);
for (int i = 0; i < n02; i++) {
s12[sa12[i]] = i + 1;
}
} else {
for (int i = 0; i < n02; i++) {
sa12[s12[i] - 1] = i;
}
}
int[] s0 = new int[n0], sa0 = new int[n0];
for (int i = 0, j = 0; i < n02; i++) {
if (sa12[i] < n0) {
s0[j++] = 3 * sa12[i];
}
}
radixPass(nums, s0, sa0, 0, n0, K);
int[] sa = new int[n];
for (int p = 0, t = n0 - n1, k = 0; k < n; k++) {
int i = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2;
int j = sa0[p];
if (sa12[t] < n0 ? leq(nums[i], s12[sa12[t] + n0], nums[j], s12[j / 3])
: leq(nums[i], nums[i + 1], s12[sa12[t] - n0 + 1], nums[j], nums[j + 1], s12[j / 3 + n0])) {
sa[k] = i;
t++;
if (t == n02) {
for (k++; p < n0; p++, k++) {
sa[k] = sa0[p];
}
}
} else {
sa[k] = j;
p++;
if (p == n0) {
for (k++; t < n02; t++, k++) {
sa[k] = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2;
}
}
}
}
return sa;
}
private void radixPass(int[] nums, int[] input, int[] output, int offset, int n, int k) {
int[] cnt = new int[k + 1];
for (int i = 0; i < n; ++i) {
cnt[nums[input[i] + offset]]++;
}
for (int i = 0, sum = 0; i < cnt.length; ++i) {
int t = cnt[i];
cnt[i] = sum;
sum += t;
}
for (int i = 0; i < n; ++i) {
output[cnt[nums[input[i] + offset]]++] = input[i];
}
}
private boolean leq(int a1, int a2, int b1, int b2) {
return a1 < b1 || (a1 == b1 && a2 <= b2);
}
private boolean leq(int a1, int a2, int a3, int b1, int b2, int b3) {
return a1 < b1 || (a1 == b1 && leq(a2, a3, b2, b3));
}
private int[] rank() {
int n = sa.length;
int[] ans = new int[n];
for (int i = 0; i < n; i++) {
ans[sa[i]] = i + 1;
}
return ans;
}
}

Binary file not shown.

@ -0,0 +1,85 @@
package class45;
public class Code01_BurstBalloons {
public static int maxCoins1(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
if (arr.length == 1) {
return arr[0];
}
int N = arr.length;
int[] help = new int[N + 2];
help[0] = 1;
help[N + 1] = 1;
for (int i = 0; i < N; i++) {
help[i + 1] = arr[i];
}
return process(help, 1, N);
}
// 打爆arr[L..R]范围上的所有气球,返回最大的分数
// 假设arr[L-1]和arr[R+1]一定没有被打爆
public static int process(int[] arr, int L, int R) {
if (L == R) {// 如果arr[L..R]范围上只有一个气球,直接打爆即可
return arr[L - 1] * arr[L] * arr[R + 1];
}
// 最后打爆arr[L]的方案和最后打爆arr[R]的方案,先比较一下
int max = Math.max(
arr[L - 1] * arr[L] * arr[R + 1] + process(arr, L + 1, R),
arr[L - 1] * arr[R] * arr[R + 1] + process(arr, L, R - 1));
// 尝试中间位置的气球最后被打爆的每一种方案
for (int i = L + 1; i < R; i++) {
max = Math.max(max,
arr[L - 1] * arr[i] * arr[R + 1] + process(arr, L, i - 1)
+ process(arr, i + 1, R));
}
return max;
}
public static int maxCoins2(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
if (arr.length == 1) {
return arr[0];
}
int N = arr.length;
int[] help = new int[N + 2];
help[0] = 1;
help[N + 1] = 1;
for (int i = 0; i < N; i++) {
help[i + 1] = arr[i];
}
int[][] dp = new int[N + 2][N + 2];
for (int i = 1; i <= N; i++) {
dp[i][i] = help[i - 1] * help[i] * help[i + 1];
System.out.println(dp[i][i]);
}
for (int L = N; L >= 1; L--) {
for (int R = L + 1; R <= N; R++) {
// 求解dp[L][R]表示help[L..R]上打爆所有气球的最大分数
// 最后打爆help[L]的方案
int finalL = help[L - 1] * help[L] * help[R + 1] + dp[L + 1][R];
// 最后打爆help[R]的方案
int finalR = help[L - 1] * help[R] * help[R + 1] + dp[L][R - 1];
// 最后打爆help[L]的方案和最后打爆help[R]的方案,先比较一下
dp[L][R] = Math.max(finalL, finalR);
// 尝试中间位置的气球最后被打爆的每一种方案
for (int i = L + 1; i < R; i++) {
dp[L][R] = Math.max(dp[L][R], help[L - 1] * help[i]
* help[R + 1] + dp[L][i - 1] + dp[i + 1][R]);
}
}
}
return dp[1][N];
}
public static void main(String[] args) {
int[] arr = { 4, 2, 3, 5, 1, 6 };
System.out.println(maxCoins1(arr));
System.out.println(maxCoins2(arr));
}
}

@ -0,0 +1,38 @@
package class45;
public class Code02_RemoveBoxes {
public int removeBoxes(int[] boxes) {
int N = boxes.length;
int[][][] dp = new int[N][N][N];
return process(boxes, 0, N - 1, 0, dp);
}
// boxes[L....R]前面还跟着K个boxes[L]
// 前面的包袱和L...R所有的数都消掉最好得分是什么
public static int process(int[] boxes, int L, int R, int K, int[][][] dp) {
if (L > R) {
return 0;
}
if (dp[L][R][K] != 0) {
return dp[L][R][K];
}
if (L == R) {
dp[L][R][K] = (K + 1) * (K + 1);
return dp[L][R][K];
}
while (L < R && boxes[L] == boxes[L + 1]) {
L++;
K++;
}
int ans = (K + 1) * (K + 1) + process(boxes, L + 1, R, 0, dp);
for (int m = L + 1; m <= R; m++) {
if (boxes[L] == boxes[m]) {
ans = Math.max(ans, process(boxes, L + 1, m - 1, 0, dp) + process(boxes, m, R, K + 1, dp));
}
}
dp[L][R][K] = ans;
return ans;
}
}

@ -0,0 +1,33 @@
package class45;
public class Code03_StrangePrinter {
public static int strangePrinter(String s) {
if (s == null || s.length() == 0) {
return 0;
}
char[] str = s.toCharArray();
int N = str.length;
int[][] dp = new int[N][N];
dp[N - 1][N - 1] = 1;
for (int i = 0; i < N - 1; i++) {
dp[i][i] = 1;
dp[i][i + 1] = str[i] == str[i + 1] ? 1 : 2;
}
for (int L = N - 3; L >= 0; L--) {
for (int R = L + 2; R < N; R++) {
// L....R
dp[L][R] = R - L + 1;
// L...k-1 k...R
for (int k = L + 1; k <= R; k++) {
dp[L][R] = Math.min(dp[L][R], dp[L][k - 1] + dp[k][R] - (str[L] == str[k] ? 1 : 0));
}
}
}
return dp[0][N - 1];
}
}

@ -0,0 +1,237 @@
package class46;
public class Code01_RestoreWays {
/*
* arrn(3 <= n <= 10^4)<=200 1. 0arr[0] <=
* arr[1] 2. n-1arr[n-1] <= arr[n-2] 3. iarr[i] <= max(arr[i-1],
* arr[i+1]) arrkk0
* arr [6,0,9] [6,9,9]1 [6,9,9]
*
* [ ...... 0 0]
*/
public static int ways0(int[] arr) {
return process0(arr, 0);
}
public static int process0(int[] arr, int index) {
if (index == arr.length) {
return isValid(arr) ? 1 : 0;
} else {
if (arr[index] != 0) {
return process0(arr, index + 1);
} else {
int ways = 0;
for (int v = 1; v < 201; v++) {
arr[index] = v;
ways += process0(arr, index + 1);
}
arr[index] = 0;
return ways;
}
}
}
public static boolean isValid(int[] arr) {
if (arr[0] > arr[1]) {
return false;
}
if (arr[arr.length - 1] > arr[arr.length - 2]) {
return false;
}
for (int i = 1; i < arr.length - 1; i++) {
if (arr[i] > Math.max(arr[i - 1], arr[i + 1])) {
return false;
}
}
return true;
}
public static int ways1(int[] arr) {
int N = arr.length;
if (arr[N - 1] != 0) {
return process1(arr, N - 1, arr[N - 1], 2);
} else {
int ways = 0;
for (int v = 1; v < 201; v++) {
ways += process1(arr, N - 1, v, 2);
}
return ways;
}
}
// 如果i位置的数字变成了v,
// 并且arr[i]和arr[i+1]的关系为s
// s==0代表arr[i] < arr[i+1] 右大
// s==1代表arr[i] == arr[i+1]
// s==2代表arr[i] > arr[i+1] 右小
// 返回0...i范围上有多少种有效的转化方式
public static int process1(int[] arr, int i, int v, int s) {
if (i == 0) { // 0...i 只剩一个数了0...0
return ((s == 0 || s == 1) && (arr[i] == 0 || v == arr[i])) ? 1 : 0;
}
// i > 0
if (arr[i] != 0 && v != arr[i]) {
return 0;
}
// i>0 并且 i位置的数真的可以变成V
int ways = 0;
if (s == 0 || s == 1) { // [i - 1] [i] <= 右
for (int pre = 1; pre < 201; pre++) {
ways += process1(arr, i - 1, pre, pre < v ? 0 : (pre == v ? 1 : 2));
}
} else { // s == 2 i > 右 i-1 >= i > 右
for (int pre = v; pre < 201; pre++) {
ways += process1(arr, i - 1, pre, pre == v ? 1 : 2);
}
}
return ways;
}
public static int ways2(int[] arr) {
int N = arr.length;
int[][][] dp = new int[N][201][3];
// dp[0][...][...]
// dp[0][...][2] = 0
// dp[0][...][0] = 1
// dp[0][...][1] = 1
if (arr[0] != 0) {
dp[0][arr[0]][0] = 1;
dp[0][arr[0]][1] = 1;
} else {
for (int v = 1; v < 201; v++) {
dp[0][v][0] = 1;
dp[0][v][1] = 1;
}
}
for (int i = 1; i < N; i++) {
for (int v = 1; v < 201; v++) {
for (int s = 0; s < 3; s++) {
if (arr[i] == 0 || v == arr[i]) {
if (s == 0 || s == 1) {
for (int pre = 1; pre < 201; pre++) {
dp[i][v][s] += dp[i - 1][pre][pre < v ? 0 : (pre == v ? 1 : 2)];
}
} else {
for (int pre = v; pre < 201; pre++) {
dp[i][v][s] += dp[i - 1][pre][pre < v ? 0 : (pre == v ? 1 : 2)];
}
}
}
}
}
}
if (arr[N - 1] != 0) {
return dp[N - 1][arr[N - 1]][2];
} else {
int ways = 0;
for (int v = 1; v < 201; v++) {
ways += dp[N - 1][v][2];
}
return ways;
}
}
public static int ways3(int[] arr) {
int N = arr.length;
int[][][] dp = new int[N][201][3];
if (arr[0] != 0) {
dp[0][arr[0]][0] = 1;
dp[0][arr[0]][1] = 1;
} else {
for (int v = 1; v < 201; v++) {
dp[0][v][0] = 1;
dp[0][v][1] = 1;
}
}
// presum[0~V][0] -> sum0[]
// presum[0~V][1] -> sum1[]
// presum[0~V][2] -> sum2[]
int[][] presum = new int[201][3];
// presum -> dp[0][..][..] 三张表
for (int v = 1; v < 201; v++) {
for (int s = 0; s < 3; s++) {
presum[v][s] = presum[v - 1][s] + dp[0][v][s];
}
}
for (int i = 1; i < N; i++) {
for (int v = 1; v < 201; v++) {
for (int s = 0; s < 3; s++) {
if (arr[i] == 0 || v == arr[i]) {
if (s == 0 || s == 1) {
// dp[i][..][..] -> dp[i-1][..][..]
dp[i][v][s] += sum(1, v - 1, 0, presum);
dp[i][v][s] += dp[i - 1][v][1];
dp[i][v][s] += sum(v + 1, 200, 2, presum);
} else {
dp[i][v][s] += dp[i - 1][v][1];
dp[i][v][s] += sum(v + 1, 200, 2, presum);
}
}
}
}
for (int v = 1; v < 201; v++) {
for (int s = 0; s < 3; s++) {
presum[v][s] = presum[v - 1][s] + dp[i][v][s];
}
}
}
if (arr[N - 1] != 0) {
return dp[N - 1][arr[N - 1]][2];
} else {
return sum(1, 200, 2, presum);
}
}
public static int sum(int begin, int end, int relation, int[][] presum) {
return presum[end][relation] - presum[begin - 1][relation];
}
// for test
public static int[] generateRandomArray(int len) {
int[] ans = new int[(int) (Math.random() * len) + 2];
for (int i = 0; i < ans.length; i++) {
if (Math.random() < 0.5) {
ans[i] = 0;
} else {
ans[i] = (int) (Math.random() * 200) + 1;
}
}
return ans;
}
// for test
public static void printArray(int[] arr) {
System.out.println("arr size : " + arr.length);
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int len = 3;
int testTime = 10;
System.out.println("test begin");
for (int i = 0; i < testTime; i++) {
int[] arr = generateRandomArray(len);
int ans0 = ways0(arr);
int ans1 = ways1(arr);
int ans2 = ways2(arr);
int ans3 = ways3(arr);
if (ans0 != ans1 || ans2 != ans3 || ans0 != ans2) {
System.out.println("Oops!");
}
}
System.out.println("test finish");
int[] arr = generateRandomArray(100000);
System.out.println(arr.length);
long begin = System.currentTimeMillis();
ways3(arr);
long end = System.currentTimeMillis();
System.out.println("run time : " + (end - begin) + " ms");
}
}

@ -0,0 +1,172 @@
package class46;
// 如果一个字符相邻的位置没有相同字符,那么这个位置的字符出现不能被消掉
// 比如:"ab"其中a和b都不能被消掉
// 如果一个字符相邻的位置有相同字符,就可以一起消掉
// 比如:"abbbc"中间一串的b是可以被消掉的消除之后剩下"ac"
// 某些字符如果消掉了,剩下的字符认为重新靠在一起
// 给定一个字符串,你可以决定每一步消除的顺序,目标是请尽可能多的消掉字符,返回最少的剩余字符数量
// 比如:"aacca", 如果先消掉最左侧的"aa",那么将剩下"cca",然后把"cc"消掉,剩下的"a"将无法再消除返回1
// 但是如果先消掉中间的"cc",那么将剩下"aaa"最后都消掉就一个字符也不剩了返回0这才是最优解。
// 再比如:"baaccabb"
// 如果先消除最左侧的两个a剩下"bccabb"
// 如果再消除最左侧的两个c剩下"babb"
// 最后消除最右侧的两个b剩下"ba"无法再消除返回2
// 而最优策略是:
// 如果先消除中间的两个c剩下"baaabb"
// 如果再消除中间的三个a剩下"bbb"
// 最后消除三个b不留下任何字符返回0这才是最优解
public class Code02_DeleteAdjacentSameCharacter {
// 暴力解
public static int restMin1(String s) {
if (s == null) {
return 0;
}
if (s.length() < 2) {
return s.length();
}
int minLen = s.length();
for (int L = 0; L < s.length(); L++) {
for (int R = L + 1; R < s.length(); R++) {
if (canDelete(s.substring(L, R + 1))) {
minLen = Math.min(minLen, restMin1(s.substring(0, L) + s.substring(R + 1, s.length())));
}
}
}
return minLen;
}
public static boolean canDelete(String s) {
char[] str = s.toCharArray();
for (int i = 1; i < str.length; i++) {
if (str[i - 1] != str[i]) {
return false;
}
}
return true;
}
// 优良尝试的暴力递归版本
public static int restMin2(String s) {
if (s == null) {
return 0;
}
if (s.length() < 2) {
return s.length();
}
char[] str = s.toCharArray();
return process(str, 0, str.length - 1, false);
}
public static int process(char[] str, int L, int R, boolean has) {
if (L > R) {
return 0;
}
if (L == R) {
return has ? 0 : 1;
}
int index = L;
int K = has ? 1 : 0;
while (index <= R && str[index] == str[L]) {
K++;
index++;
}
int way1 = (K > 1 ? 0 : 1) + process(str, index, R, false);
int way2 = Integer.MAX_VALUE;
for (int split = index; split <= R; split++) {
if (str[split] == str[L] && str[split] != str[split - 1]) {
if (process(str, index, split - 1, false) == 0) {
way2 = Math.min(way2, process(str, split, R, K != 0));
}
}
}
return Math.min(way1, way2);
}
// 优良尝试的动态规划版本
public static int restMin3(String s) {
if (s == null) {
return 0;
}
if (s.length() < 2) {
return s.length();
}
char[] str = s.toCharArray();
int N = str.length;
int[][][] dp = new int[N][N][2];
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
for (int k = 0; k < 2; k++) {
dp[i][j][k] = -1;
}
}
}
return dpProcess(str, 0, N - 1, false, dp);
}
public static int dpProcess(char[] str, int L, int R, boolean has, int[][][] dp) {
if (L > R) {
return 0;
}
int K = has ? 1 : 0;
if (dp[L][R][K] != -1) {
return dp[L][R][K];
}
int ans = 0;
if (L == R) {
ans = (K == 0 ? 1 : 0);
} else {
int index = L;
int all = K;
while (index <= R && str[index] == str[L]) {
all++;
index++;
}
int way1 = (all > 1 ? 0 : 1) + dpProcess(str, index, R, false, dp);
int way2 = Integer.MAX_VALUE;
for (int split = index; split <= R; split++) {
if (str[split] == str[L] && str[split] != str[split - 1]) {
if (dpProcess(str, index, split - 1, false, dp) == 0) {
way2 = Math.min(way2, dpProcess(str, split, R, all > 0, dp));
}
}
}
ans = Math.min(way1, way2);
}
dp[L][R][K] = ans;
return ans;
}
public static String randomString(int len, int variety) {
char[] str = new char[len];
for (int i = 0; i < len; i++) {
str[i] = (char) ((int) (Math.random() * variety) + 'a');
}
return String.valueOf(str);
}
public static void main(String[] args) {
int maxLen = 16;
int variety = 3;
int testTime = 100000;
System.out.println("测试开始");
for (int i = 0; i < testTime; i++) {
int len = (int) (Math.random() * maxLen);
String str = randomString(len, variety);
int ans1 = restMin1(str);
int ans2 = restMin2(str);
int ans3 = restMin3(str);
if (ans1 != ans2 || ans1 != ans3) {
System.out.println(str);
System.out.println(ans1);
System.out.println(ans2);
System.out.println(ans3);
System.out.println("出错了!");
break;
}
}
System.out.println("测试结束");
}
}
Loading…
Cancel
Save