modify code

master
algorithmzuo 3 years ago
parent 937e9cd44f
commit 8f86603ea8

@ -0,0 +1,66 @@
package class_2022_12_4_week;
// 给你一个 m x n 的二进制矩阵 grid
// 每个格子要么为 0 (空)要么为 1 (被占据)
// 给你邮票的尺寸为 stampHeight x stampWidth
// 我们想将邮票贴进二进制矩阵中,且满足以下 限制 和 要求
// 覆盖所有空格子,不覆盖任何被占据的格子
// 可以放入任意数目的邮票,邮票可以相互有重叠部分
// 邮票不允许旋转,邮票必须完全在矩阵内
// 如果在满足上述要求的前提下,可以放入邮票,请返回 true ,否则返回 false
// 测试链接 : https://leetcode.cn/problems/stamping-the-grid/
public class Code01_StampingTheGrid {
public static boolean possibleToStamp(int[][] grid, int h, int w) {
int n = grid.length;
int m = grid[0].length;
// 左上累加和矩阵
int[][] sum = new int[n + 1][m + 1];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
sum[i + 1][j + 1] = grid[i][j];
}
}
build(sum);
int[][] diff = new int[n + 2][m + 2];
for (int a = 1, c = a + h - 1; c <= n; a++, c++) {
for (int b = 1, d = b + w - 1; d <= m; b++, d++) {
if (empty(sum, a, b, c, d)) {
set(diff, a, b, c, d);
}
}
}
build(diff);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == 0 && diff[i + 1][j + 1] == 0) {
return false;
}
}
}
return true;
}
public static void build(int[][] m) {
for (int i = 1; i < m.length; i++) {
for (int j = 1; j < m[0].length; j++) {
m[i][j] += m[i - 1][j] + m[i][j - 1] - m[i - 1][j - 1];
}
}
}
public static boolean empty(int[][] sum, int a, int b, int c, int d) {
return sum[c][d] - sum[c][b - 1] - sum[a - 1][d] + sum[a - 1][b - 1] == 0;
}
// (3,4) (8,9)
// 3,4 +1
public static void set(int[][] diff, int a, int b, int c, int d) {
diff[a][b] += 1;
diff[c + 1][d + 1] += 1;
diff[c + 1][b] -= 1;
diff[a][d + 1] -= 1;
}
}

@ -0,0 +1,79 @@
package class_2022_12_4_week;
// Floyd算法解析
// 本题测试链接 : https://www.luogu.com.cn/problem/P2910
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交如下方法把主类名改成Main可以直接通过
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
public class Code02_ClearAndPresentDanger {
public static int N = 100;
public static int M = 10000;
public static int[] path = new int[M];
public static int[][] distance = new int[N][N];
public static int n, m, ans;
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));
while (in.nextToken() != StreamTokenizer.TT_EOF) {
n = (int) in.nval;
in.nextToken();
m = (int) in.nval;
for (int i = 0; i < m; i++) {
in.nextToken();
path[i] = (int) in.nval - 1;
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
in.nextToken();
distance[i][j] = (int) in.nval;
}
}
floyd();
ans = 0;
for (int i = 1; i < m; i++) {
ans += distance[path[i - 1]][path[i]];
}
out.println(ans);
out.flush();
}
}
public static void floyd() {
// O(N^3)的过程
// 枚举每个跳板
// 注意! 跳板要最先枚举然后是from和to
for (int jump = 0; jump < n; jump++) { // 中途!
for (int from = 0; from < n; from++) { // from
for (int to = 0; to < n; to++) { // to
if (distance[from][jump] != Integer.MAX_VALUE && distance[jump][to] != Integer.MAX_VALUE
&& distance[from][to] > distance[from][jump] + distance[jump][to]) {
distance[from][to] = distance[from][jump] + distance[jump][to];
}
}
}
}
// 如下这么写是错的
// for (int from = 0; from < n; from++) {
// for (int to = 0; to < n; to++) {
// for (int jump = 0; jump < n; jump++) {
// if (distance[from][jump] != Integer.MAX_VALUE && distance[jump][to] != Integer.MAX_VALUE
// && distance[from][to] > distance[from][jump] + distance[jump][to]) {
// distance[from][to] = distance[from][jump] + distance[jump][to];
// }
// }
// }
// }
}
}

@ -0,0 +1,99 @@
package class_2022_12_4_week;
// Floyd算法应用
// 存在一个由 n 个节点组成的无向连通图,图中的节点按从 0 到 n - 1 编号
// 给你一个数组 graph 表示这个图
// 其中graph[i] 是一个列表,由所有与节点 i 直接相连的节点组成
// 返回能够访问所有节点的最短路径的长度
// 你可以在任一节点开始和停止,也可以多次重访节点,并且可以重用边
// 测试链接 : https://leetcode.cn/problems/shortest-path-visiting-all-nodes/
public class Code03_ShortestPathVisitingAllNodes {
public static int shortestPathLength(int[][] graph) {
int n = graph.length;
int[][] distance = floyd(n, graph);
int ans = Integer.MAX_VALUE;
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;
}
}
for (int i = 0; i < n; i++) {
ans = Math.min(ans, process(1 << i, i, n, distance, dp));
}
return ans;
}
public static int[][] floyd(int n, int[][] graph) {
int[][] distance = new int[n][n];
// 初始化认为都没路
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
distance[i][j] = Integer.MAX_VALUE;
}
}
// 自己到自己的距离为0
for (int i = 0; i < n; i++) {
distance[i][i] = 0;
}
// 支持任意有向图,把直接边先填入
for (int cur = 0; cur < n; cur++) {
for (int next : graph[cur]) {
distance[cur][next] = 1;
distance[next][cur] = 1;
}
}
// O(N^3)的过程
// 枚举每个跳板
// 注意! 跳板要最先枚举然后是from和to
for (int jump = 0; jump < n; jump++) {
for (int from = 0; from < n; from++) {
for (int to = 0; to < n; to++) {
if (distance[from][jump] != Integer.MAX_VALUE && distance[jump][to] != Integer.MAX_VALUE
&& distance[from][to] > distance[from][jump] + distance[jump][to]) {
distance[from][to] = distance[from][jump] + distance[jump][to];
}
}
}
}
return distance;
}
// status : 所有已经走过点的状态
// 0 1 2 3 4 5
// int
// 5 4 3 2 1 0
// 0 0 1 1 0 1
// cur : 当前所在哪个城市!
// n : 一共有几座城
// 返回值 : 从此时开始,逛掉所有的城市,至少还要走的路,返回!
public static int process(int status, int cur, int n, int[][] distance, int[][] dp) {
// 5 4 3 2 1 0
// 1 1 1 1 1 1
// 1 << 6 - 1
if (status == (1 << n) - 1) {
return 0;
}
if (dp[status][cur] != -1) {
return dp[status][cur];
}
int ans = Integer.MAX_VALUE;
// status:
// 5 4 3 2 1 0
// 0 0 1 0 1 1
// cur : 0
// next : 2 4 5
for (int next = 0; next < n; next++) {
if ((status & (1 << next)) == 0 && distance[cur][next] != Integer.MAX_VALUE) {
int nextAns = process(status | (1 << next), next, n, distance, dp);
if (nextAns != Integer.MAX_VALUE) {
ans = Math.min(ans, distance[cur][next] + nextAns);
}
}
}
dp[status][cur] = ans;
return ans;
}
}

@ -0,0 +1,84 @@
package class_2022_12_4_week;
// 你现在手里有一份大小为 n x n 的 网格 grid
// 上面的每个 单元格 都用 0 和 1 标记好了其中 0 代表海洋1 代表陆地。
// 请你找出一个海洋单元格,这个海洋单元格到离它最近的陆地单元格的距离是最大的
// 并返回该距离。如果网格上只有陆地或者海洋,请返回 -1。
// 我们这里说的距离是「曼哈顿距离」( Manhattan Distance
// (x0, y0) 和 (x1, y1) 这两个单元格之间的距离是 |x0 - x1| + |y0 - y1| 。
// 测试链接 : https://leetcode.cn/problems/as-far-from-land-as-possible/
public class Code04_AsFarFromLandAsPossible {
// 队列接受一个东西,比如(i,j)就加到r位置
// queue[r][0] = i
// queue[r++][1] = j
// 队列弹出一个东西就把l位置的东西弹出
public static int[][] queue = new int[10000][2];
public static int l;
public static int r;
// 一个东西进入了队列,比如(i,j)进入了visited[i][j] = true
// 如果(i,j)没进入过visited[i][j] = false
public static boolean[][] visited = new boolean[100][100];
// find表示发现了多少海洋
public static int find;
public static int maxDistance(int[][] grid) {
// 清空变量
// 只要l = 0r = 0队列就算被清空了
l = 0;
r = 0;
find = 0;
int n = grid.length;
int m = grid[0].length;
// 清空visited
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
visited[i][j] = false;
}
}
// 大体思路 :
// 1) 先把所有的陆地加入队列,并且统计一共有多少海洋
int seas = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == 1) {
visited[i][j] = true;
queue[r][0] = i;
queue[r++][1] = j;
} else {
seas++;
}
}
}
// 2) 从陆地开始广播出去(bfs),每一块陆地的上、下、左、右所能找到的海洋都是第一层海洋
// 3) 第一层海洋继续bfs每一块海洋的上、下、左、右所能找到的海洋都是第二层海洋
// 4) 第二层海洋继续bfs每一块海洋的上、下、左、右所能找到的海洋都是第三层海洋
// ...
// 也就是说以陆地做起点每一层bfs都只找海洋
// 看看最深能找到多少层海洋
int distance = 0; // 这个变量就是最深的海洋层数
while (l < r && find < seas) { // find < seas说明所有的海洋块没有找全继续找
int size = r - l;
for (int i = 0; i < size && find < seas; i++, l++) {
int row = queue[l][0];
int col = queue[l][1];
add(row - 1, col, n, m, grid);
add(row + 1, col, n, m, grid);
add(row, col - 1, n, m, grid);
add(row, col + 1, n, m, grid);
}
distance++;
}
return find == 0 ? -1 : distance;
}
public static void add(int i, int j, int n, int m, int[][] grid) {
if (i >= 0 && i < n && j >= 0 && j < m && grid[i][j] == 0 && !visited[i][j]) {
find++;
visited[i][j] = true;
queue[r][0] = i;
queue[r++][1] = j;
}
}
}

@ -0,0 +1,107 @@
package class_2022_12_4_week;
import java.util.PriorityQueue;
// 你的赛车可以从位置 0 开始,并且速度为 +1 ,在一条无限长的数轴上行驶
// 赛车也可以向负方向行驶
// 赛车可以按照由加速指令 'A' 和倒车指令 'R' 组成的指令序列自动行驶。
// 当收到指令 'A' 时,赛车这样行驶:
// position += speed
// speed *= 2
// 当收到指令 'R' 时,赛车这样行驶:
// 如果速度为正数那么speed = -1
// 否则 speed = 1
// 当前所处位置不变。
// 例如,在执行指令 "AAR" 后,赛车位置变化为 0 --> 1 --> 3 --> 3
// 速度变化为 1 --> 2 --> 4 --> -1
// 给你一个目标位置 target ,返回能到达目标位置的最短指令序列的长度。
// 测试链接 : https://leetcode.cn/problems/race-car/
public class Code05_RaceCar {
// Dijkstra算法
public static int racecar1(int target) {
int maxp = 0;
int maxs = 1;
while (maxp <= target) {
maxp += 1 << (maxs - 1);
maxs++;
}
// 0 : 几倍速
// 1 : 花费了几步
// 2 : 当前位置
PriorityQueue<int[]> heap = new PriorityQueue<>((a, b) -> a[1] - b[1]);
boolean[][] positive = new boolean[maxs + 1][maxp + 1];
boolean[][] negative = new boolean[maxs + 1][maxp + 1];
heap.add(new int[] { 1, 0, 0 });
while (!heap.isEmpty()) {
int[] cur = heap.poll();
int speed = cur[0];
int cost = cur[1];
int position = cur[2];
if (position == target) {
return cost;
}
if (speed > 0) {
if (positive[speed][position]) {
continue;
}
positive[speed][position] = true;
add(speed + 1, cost + 1, position + (1 << (speed - 1)), maxp, heap, positive);
add(-1, cost + 1, position, maxp, heap, negative);
} else {
speed = -speed;
if (negative[speed][position]) {
continue;
}
negative[speed][position] = true;
add(-speed - 1, cost + 1, position - (1 << (speed - 1)), maxp, heap, negative);
add(1, cost + 1, position, maxp, heap, positive);
}
}
return -1;
}
public static void add(int speed, int cost, int position, int limit, PriorityQueue<int[]> heap,
boolean[][] visited) {
if (position >= 0 && position <= limit && !visited[Math.abs(speed)][position]) {
heap.add(new int[] { speed, cost, position });
}
}
// 动态规划 + 数学
public static int racecar2(int target) {
int[] dp = new int[target + 1];
return process(target, dp);
}
public static int process(int target, int[] dp) {
if (dp[target] > 0) {
return dp[target];
}
int steps = 0;
int speed = 1;
while (speed <= target) {
speed <<= 1;
steps++;
}
int ans = 0;
int beyond = speed - 1 - target;
if (beyond == 0) {
ans = steps;
} else {
ans = steps + 1 + process(beyond, dp);
steps--;
speed >>= 1;
int lack = target - (speed - 1);
int offset = 1;
for (int back = 0; back < steps; back++) {
ans = Math.min(ans, steps + 1 + back + 1 + process(lack, dp));
lack += offset;
offset <<= 1;
}
}
dp[target] = ans;
return ans;
}
}

@ -0,0 +1,83 @@
package class_2022_12_4_week;
import java.util.HashSet;
import java.util.PriorityQueue;
// 对于某些非负整数 k ,如果交换 s1 中两个字母的位置恰好 k 次
// 能够使结果字符串等于 s2 ,则认为字符串 s1 和 s2 的 相似度为 k
// 给你两个字母异位词 s1 和 s2 ,返回 s1 和 s2 的相似度 k 的最小值
// 测试链接 : https://leetcode.cn/problems/k-similar-strings/
public class Code06_KSimilarStrings {
public static class Node {
public int cost; // 代价,已经换了几回了!
public int guess;// 猜测还要换几回,能变对!
public int where;// 有必须去比对的下标,左边不再换了!
public String str; // 当前的字符
public Node(int r, int g, int i, String s) {
cost = r;
guess = g;
where = i;
str = s;
}
}
public static int kSimilarity(String s1, String s2) {
int len = s1.length();
PriorityQueue<Node> heap = new PriorityQueue<>((a, b) -> (a.cost + a.guess) - (b.cost + b.guess));
HashSet<String> visited = new HashSet<>();
heap.add(new Node(0, 0, 0, s1));
while (!heap.isEmpty()) {
Node cur = heap.poll();
if (visited.contains(cur.str)) {
continue;
}
if (cur.str.equals(s2)) {
return cur.cost;
}
visited.add(cur.str);
int firstDiff = cur.where;
while (cur.str.charAt(firstDiff) == s2.charAt(firstDiff)) {
firstDiff++;
}
char[] curStr = cur.str.toCharArray();
for (int i = firstDiff + 1; i < len; i++) {
if (curStr[i] == s2.charAt(firstDiff) && curStr[i] != s2.charAt(i)) {
swap(curStr, firstDiff, i);
add(String.valueOf(curStr), s2, cur.cost + 1, firstDiff + 1, heap, visited);
swap(curStr, firstDiff, i);
}
}
}
return -1;
}
public static void swap(char[] s, int i, int j) {
char tmp = s[i];
s[i] = s[j];
s[j] = tmp;
}
public static void add(String add, String s2, int cost, int index, PriorityQueue<Node> heap,
HashSet<String> visited) {
if (!visited.contains(add)) {
heap.add(new Node(cost, evaluate(add, s2, index), index, add));
}
}
// 估值函数
// 看每周有营养的大厂算法面试题2022年1月第3周
// 估值函数的估计值要绝对 <= 真实距离
// 但又要确保估计值足够大足够接近真实距离,这样效果最好
public static int evaluate(String s1, String s2, int index) {
int diff = 0;
for (int i = index; i < s1.length(); i++) {
if (s1.charAt(i) != s2.charAt(i)) {
diff++;
}
}
return (diff + 1) / 2;
}
}

@ -2707,4 +2707,60 @@ number调用次数 <= 100000
第053节 2022年12月第4周流行算法题目解析
给你一个 m x n 的二进制矩阵 grid
每个格子要么为 0 (空)要么为 1 (被占据)
给你邮票的尺寸为 stampHeight x stampWidth
我们想将邮票贴进二进制矩阵中,且满足以下 限制 和 要求
覆盖所有空格子,不覆盖任何被占据的格子
可以放入任意数目的邮票,邮票可以相互有重叠部分
邮票不允许旋转,邮票必须完全在矩阵内
如果在满足上述要求的前提下,可以放入邮票,请返回 true ,否则返回 false
测试链接 : https://leetcode.cn/problems/stamping-the-grid/
Floyd算法解析
本题测试链接 : https://www.luogu.com.cn/problem/P2910
Floyd算法应用
存在一个由 n 个节点组成的无向连通图,图中的节点按从 0 到 n - 1 编号
给你一个数组 graph 表示这个图
其中graph[i] 是一个列表,由所有与节点 i 直接相连的节点组成
返回能够访问所有节点的最短路径的长度
你可以在任一节点开始和停止,也可以多次重访节点,并且可以重用边
测试链接 : https://leetcode.cn/problems/shortest-path-visiting-all-nodes/
你现在手里有一份大小为 n x n 的 网格 grid
上面的每个 单元格 都用 0 和 1 标记好了其中 0 代表海洋1 代表陆地。
请你找出一个海洋单元格,这个海洋单元格到离它最近的陆地单元格的距离是最大的
并返回该距离。如果网格上只有陆地或者海洋,请返回 -1。
我们这里说的距离是「曼哈顿距离」( Manhattan Distance
(x0, y0) 和 (x1, y1) 这两个单元格之间的距离是 |x0 - x1| + |y0 - y1| 。
测试链接 : https://leetcode.cn/problems/as-far-from-land-as-possible/
你的赛车可以从位置 0 开始,并且速度为 +1 ,在一条无限长的数轴上行驶
赛车也可以向负方向行驶
赛车可以按照由加速指令 'A' 和倒车指令 'R' 组成的指令序列自动行驶。
当收到指令 'A' 时,赛车这样行驶:
position += speed
speed *= 2
当收到指令 'R' 时,赛车这样行驶:
如果速度为正数那么speed = -1
否则 speed = 1
当前所处位置不变。
例如,在执行指令 "AAR" 后,赛车位置变化为 0 --> 1 --> 3 --> 3
速度变化为 1 --> 2 --> 4 --> -1
给你一个目标位置 target ,返回能到达目标位置的最短指令序列的长度。
测试链接 : https://leetcode.cn/problems/race-car/
对于某些非负整数 k ,如果交换 s1 中两个字母的位置恰好 k 次
能够使结果字符串等于 s2 ,则认为字符串 s1 和 s2 的 相似度为 k
给你两个字母异位词 s1 和 s2 ,返回 s1 和 s2 的相似度 k 的最小值
测试链接 : https://leetcode.cn/problems/k-similar-strings/

Loading…
Cancel
Save