modify code

master
algorithmzuo 1 year ago
parent 28125c4d9c
commit 125fba9849

@ -0,0 +1,113 @@
package class_2023_07_1_week;
// 先来一个最近国外同学考的题目
// 已知一些供应点的位置一共n个供应点
// 其中有n-1个供应点一定都在x轴上比如(15,0)位置,(2,0)位置等
// 只有1个供应点不在x轴上比如(23,17)位置
// 给出每个供应点的位置并且给定第k号供应点是出发点
// 要求每个供应点最多走过2次返回从k点出发走完所有供应点的最少距离
// 上面这个题没有代码实现
// 因为这个题就是彻底的业务分析,只有一系列的贪心设计,代码也不难写
// 同时这个题没有给出数据量但是课上会讲O(n)的方法,所以也就无所谓了
// 以下是这节课的正式题,来自学员问题
// 现在有一个打怪类型的游戏这个游戏是这样的你有n个技能
// 每一个技能会有一个伤害,
// 同时若怪物小于等于一定的血量,则该技能可能造成双倍伤害
// 每一个技能最多只能释放一次已知怪物有m点血量
// 现在想问你最少用几个技能能消灭掉他(血量小于等于0)
// 技能的数量是n怪物的血量是m
// i号技能的伤害是x[i]i号技能触发双倍伤害的血量最小值是y[i]
// 1 <= n <= 10
// 1 <= m、x[i]、y[i] <= 10^6
// 测试链接 : https://www.nowcoder.com/questionTerminal/d88ef50f8dab4850be8cd4b95514bbbd
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的所有代码,并把主类名改成"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 Code01_KillMonsterEverySkillUseOnce {
public static int MAXN = 11;
public static int[] kill = new int[MAXN];
public static int[] blood = new int[MAXN];
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) {
int t = (int) in.nval;
for (int i = 0; i < t; i++) {
in.nextToken();
int n = (int) in.nval;
in.nextToken();
int m = (int) in.nval;
for (int j = 0; j < n; j++) {
in.nextToken();
kill[j] = (int) in.nval;
in.nextToken();
blood[j] = (int) in.nval;
}
int ans = f(n, 0, m);
out.println(ans == Integer.MAX_VALUE ? -1 : ans);
out.flush();
}
}
}
// n : 固定参数,一共有几个技能
// kill: 技能伤害
// blood : 血<=多少,触发双倍伤害
// rest : 怪兽的剩余血量
// i : 可变参数,全排列代码
// kill[0...i-1] 使用过的技能顺序就是从0到i-1
// blood[0...i-1] 使用过的技能顺序就是从0到i-1
// i .... 最后,是还没有使用过的技能
// 每一个没有使用过的技能,全试一遍
public static int f(int n, int i, int rest) {
// 0 1 2 3 i(4) .. n-1
if (rest <= 0) {
return i;
}
// 血 > 0
if (i == n) {
return Integer.MAX_VALUE;
}
// 血 > 0 还有技能
// ..... (y,i,s,k,e)
// ..... y....
// ..... i....
int ans = Integer.MAX_VALUE;
for (int j = i; j < n; j++) {
// j == i. i.....n-1
// j 当前要释放的技能是谁!
swap(i, j);
if (rest > blood[i]) {
ans = Math.min(ans, f(n, i + 1, rest - kill[i]));
} else {
ans = Math.min(ans, f(n, i + 1, rest - kill[i] * 2));
}
swap(i, j);
}
return ans;
}
public static void swap(int i, int j) {
int a = kill[i];
int b = blood[i];
kill[i] = kill[j];
blood[i] = blood[j];
kill[j] = a;
blood[j] = b;
}
}

@ -0,0 +1,133 @@
package class_2023_07_1_week;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
// 给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。
// s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。
// 例如,如果 words = ["ab","cd","ef"]
// 那么 "abcdef" "abefcd""cdabef"
// "cdefab""efabcd" 和 "efcdab" 都是串联子串
// "acdbef" 不是串联子串,因为他不是任何 words 排列的连接。
// 返回所有串联字串在 s 中的开始索引
// 你可以以 任意顺序 返回答案
// 1 <= s.length <= 10^4
// 1 <= words.length <= 5000
// 1 <= words[i].length <= 30
// words[i] 和 s 由小写英文字母组成
// 测试链接 : https://leetcode.cn/problems/substring-with-concatenation-of-all-words/
public class Code02_SubstringWithConcatenationOfAllWords {
// 用字符串哈希做,时间复杂度才能到最优
// 如果s的长度为nwords里所有单词的总长度为m
// 时间复杂度O(n + m),最优解的时间复杂度与单词个数、单词长度是无关的
// 所有题解都没有做到这个复杂度的
// 虽然这个做法打败比例没有到100%,但那是因为数据量不够大
// 所以最优解的时间复杂度优势没有体现出来
// 选一个质数做进制数
public static int BASE = 499;
// 计算一个字符串的哈希值
public static long hashValue(String str) {
if (str.equals("")) {
return 0;
}
int n = str.length();
long ans = str.charAt(0) - 'a' + 1;
for (int j = 1; j < n; j++) {
ans = ans * BASE + str.charAt(j) - 'a' + 1;
}
return ans;
}
// 字符串最大长度
// 以下内容看字符串哈希的内容
public static int MAXN = 10001;
public static long[] pow = new long[MAXN];
static {
pow[0] = 1;
for (int j = 1; j < MAXN; j++) {
pow[j] = pow[j - 1] * BASE;
}
}
public static long[] hash = new long[MAXN];
public static void buildHash(String str) {
hash[0] = str.charAt(0) - 'a' + 1;
for (int j = 1; j < str.length(); j++) {
hash[j] = hash[j - 1] * BASE + str.charAt(j) - 'a' + 1;
}
}
// 范围是[l,r),左闭右开
public static long hashValue(int l, int r) {
long ans = hash[r - 1];
ans -= l == 0 ? 0 : (hash[l - 1] * pow[r - l]);
return ans;
}
public static List<Integer> findSubstring(String s, String[] words) {
List<Integer> ans = new ArrayList<>();
if (s == null || s.length() == 0 || words == null || words.length == 0) {
return ans;
}
// "abc" : 2
// "fct" : 1
// Long : string -> long
HashMap<Long, Integer> map = new HashMap<>();
for (String key : words) {
long v = hashValue(key);
map.put(v, map.getOrDefault(v, 0) + 1);
}
buildHash(s);
int n = s.length();
int wordLen = words[0].length();
int wordNum = words.length;
int allLen = wordLen * wordNum;
HashMap<Long, Integer> window = new HashMap<>();
for (int init = 0; init < wordLen && init + allLen <= n; init++) {
// [0...5) [5...10) [10...15) ...
// [1...6) [6...11) [11...16) ...
// [2...7) [7...12) [12...17) ...
int debt = wordNum;
// [0...5) [5...10) [10...15)
for (int l = init, r = init + wordLen, part = 0; part < wordNum; l += wordLen, r += wordLen, part++) {
long cur = hashValue(l, r);
window.put(cur, window.getOrDefault(cur, 0) + 1);
if (window.get(cur) <= map.getOrDefault(cur, 0)) {
debt--;
}
}
if (debt == 0) {
ans.add(init);
}
// [5...10) [10...15) [15...20)
// [10...15) [15...20)[20...25]
for (int l1 = init, r1 = init + wordLen, l2 = init + allLen,
r2 = init + allLen + wordLen; r2 <= n;
l1 += wordLen, r1 += wordLen, l2 += wordLen, r2 += wordLen) {
// l1...r1 ....... l2...r2
long out = hashValue(l1, r1);
long in = hashValue(l2, r2);
window.put(out, window.get(out) - 1);
if (window.get(out) < map.getOrDefault(out, 0)) {
debt++;
}
window.put(in, window.getOrDefault(in, 0) + 1);
if (window.get(in) <= map.getOrDefault(in, 0)) {
debt--;
}
if (debt == 0) {
ans.add(r1);
}
}
window.clear();
}
return ans;
}
}

@ -0,0 +1,88 @@
package class_2023_07_1_week;
import java.util.Arrays;
// 小扣在探索丛林的过程中,无意间发现了传说中"落寞的黄金之都"
// 而在这片建筑废墟的地带中,小扣使用探测仪监测到了存在某种带有「祝福」效果的力场
// 经过不断的勘测记录,小扣将所有力场的分布都记录了下来
// forceField[i] = [x,y,side]
// 表示第 i 片力场将覆盖以坐标 (x,y) 为中心,边长为 side 的正方形区域。
// 若任意一点的 力场强度 等于覆盖该点的力场数量
// 请求出在这片地带中 力场强度 最强处的 力场强度
// 注意:力场范围的边缘同样被力场覆盖。
// 测试链接 : https://leetcode.cn/problems/xepqZ5/
public class Code03_StrongestForceField {
public static int fieldOfGreatestBlessing(int[][] fields) {
int n = fields.length;
// n : 矩形的个数x 2*n个坐标
long[] xs = new long[n << 1];
long[] ys = new long[n << 1];
for (int i = 0, k = 0, p = 0; i < n; i++) {
long x = fields[i][0];
long y = fields[i][1];
long r = fields[i][2];
xs[k++] = (x << 1) - r;
xs[k++] = (x << 1) + r;
ys[p++] = (y << 1) - r;
ys[p++] = (y << 1) + r;
}
int sizex = sort(xs);
int sizey = sort(ys);
int[][] diff = new int[sizex + 2][sizey + 2];
for (int i = 0, a, b, c, d; i < n; i++) {
long x = fields[i][0];
long y = fields[i][1];
long r = fields[i][2];
a = rank(xs, (x << 1) - r, sizex);
b = rank(ys, (y << 1) - r, sizey);
c = rank(xs, (x << 1) + r, sizex);
d = rank(ys, (y << 1) + r, sizey);
set(diff, a, b, c, d);
}
int ans = 0;
for (int i = 1; i < diff.length; i++) {
for (int j = 1; j < diff[0].length; j++) {
diff[i][j] += diff[i - 1][j] + diff[i][j - 1] - diff[i - 1][j - 1];
ans = Math.max(ans, diff[i][j]);
}
}
return ans;
}
public static int sort(long[] nums) {
Arrays.sort(nums);
int size = 1;
for (int i = 1; i < nums.length; i++) {
if (nums[i] != nums[size - 1]) {
nums[size++] = nums[i];
}
}
return size;
}
public static int rank(long[] nums, long v, int size) {
int l = 0;
int r = size - 1;
int m, ans = 0;
while (l <= r) {
m = (l + r) / 2;
if (nums[m] >= v) {
ans = m;
r = m - 1;
} else {
l = m + 1;
}
}
return ans + 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,142 @@
package class_2023_07_1_week;
// 来自网易
// 题目出处 : https://leetcode.cn/circle/discuss/uOnnUA/
// 已知一个n*n的01矩阵
// 只能通过通过行交换、或者列交换的方式调整矩阵,
// 判断这个矩阵的对角线是否能全为1如果能返回true不能返回false
// 我们升级一下:
// 已知一个n*n的01矩阵
// 只能通过通过行交换、或者列交换的方式调整矩阵,
// 判断这个矩阵的对角线是否能全为1如果不能打印-1
// 如果能,打印需要交换的次数,并且打印怎么交换
// 测试链接 : http://acm.hdu.edu.cn/showproblem.php?pid=2819
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的所有代码,并把主类名改成"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;
import java.util.Arrays;
public class Code04_SwapRowOrColMakeDiagonalAllOne {
public static int[][] out = new int[1000][2];
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StreamTokenizer pin = new StreamTokenizer(br);
PrintWriter pout = new PrintWriter(new OutputStreamWriter(System.out));
while (pin.nextToken() != StreamTokenizer.TT_EOF) {
int n = (int) pin.nval;
int[][] graph = new int[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
pin.nextToken();
graph[i][j] = (int) pin.nval;
}
}
int t = km(graph);
pout.println(t);
for (int i = 0; i < t; i++) {
pout.println("R " + (out[i][0] + 1) + " " + (out[i][1] + 1));
}
pout.flush();
}
}
// 改写的km算法
// 如果达成的最优匹配收益 < N返回-1;
// 如果达成的最优匹配收益 == N加工好如何交换的结果(全局out数组)
// 最终返回交换的次数(>=0);
public static int km(int[][] graph) {
int N = graph.length;
int[] lx = new int[N];
int[] ly = new int[N];
int[] match = new int[N];
boolean[] x = new boolean[N];
boolean[] y = new boolean[N];
int[] slack = new int[N];
int invalid = Integer.MAX_VALUE;
for (int i = 0; i < N; i++) {
match[i] = -1;
lx[i] = -invalid;
for (int j = 0; j < N; j++) {
lx[i] = Math.max(lx[i], graph[i][j]);
}
ly[i] = 0;
}
for (int from = 0; from < N; from++) {
for (int i = 0; i < N; i++) {
slack[i] = invalid;
}
Arrays.fill(x, false);
Arrays.fill(y, false);
while (!dfs(from, x, y, lx, ly, match, slack, graph)) {
int d = invalid;
for (int i = 0; i < N; i++) {
if (!y[i] && slack[i] < d) {
d = slack[i];
}
}
for (int i = 0; i < N; i++) {
if (x[i]) {
lx[i] = lx[i] - d;
}
if (y[i]) {
ly[i] = ly[i] + d;
}
}
Arrays.fill(x, false);
Arrays.fill(y, false);
}
}
int ans = 0;
for (int i = 0; i < N; i++) {
ans += (lx[i] + ly[i]);
}
if (ans < N) {
return -1;
}
int t = 0;
for (int i = 0; i < N; i++) {
int u = match[i], v = i;
if (u != v) {
out[t][0] = v;
out[t++][1] = u;
for (int j = i + 1; j < N; j++) {
if (match[j] == v) {
match[j] = u;
}
}
}
}
return t;
}
public static boolean dfs(int from, boolean[] x, boolean[] y, int[] lx, int[] ly, int[] match, int[] slack,
int[][] map) {
int N = map.length;
x[from] = true;
for (int to = 0; to < N; to++) {
if (!y[to]) {
int d = lx[from] + ly[to] - map[from][to];
if (d != 0) {
slack[to] = Math.min(slack[to], d);
} else {
y[to] = true;
if (match[to] == -1 || dfs(match[to], x, y, lx, ly, match, slack, map)) {
match[to] = from;
return true;
}
}
}
}
return false;
}
}

@ -4013,6 +4013,66 @@ Oliver 想要你告诉他,他们最少要花费多少时间,才能使所有
第075节 2023年7月1周流行算法题目解析
先来一个最近国外同学考的题目
已知一些供应点的位置一共n个供应点
其中有n-1个供应点一定都在x轴上比如(15,0)位置,(2,0)位置等
只有1个供应点不在x轴上比如(23,17)位置
给出每个供应点的位置并且给定第k号供应点是出发点
要求每个供应点最多走过2次返回从k点出发走完所有供应点的最少距离
上面这个题没有代码实现
因为这个题就是彻底的业务分析,只有一系列的贪心设计,代码也不难写
同时这个题没有给出数据量但是课上会讲O(n)的方法,所以也就无所谓了
以下是这节课的正式题,来自学员问题
现在有一个打怪类型的游戏这个游戏是这样的你有n个技能
每一个技能会有一个伤害,
同时若怪物小于等于一定的血量,则该技能可能造成双倍伤害
每一个技能最多只能释放一次已知怪物有m点血量
现在想问你最少用几个技能能消灭掉他(血量小于等于0)
技能的数量是n怪物的血量是m
i号技能的伤害是x[i]i号技能触发双倍伤害的血量最小值是y[i]
1 <= n <= 10
1 <= m、x[i]、y[i] <= 10^6
测试链接 : https://www.nowcoder.com/questionTerminal/d88ef50f8dab4850be8cd4b95514bbbd
给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。
s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。
例如,如果 words = ["ab","cd","ef"]
那么 "abcdef" "abefcd""cdabef"
"cdefab""efabcd" 和 "efcdab" 都是串联子串
"acdbef" 不是串联子串,因为他不是任何 words 排列的连接。
返回所有串联字串在 s 中的开始索引
你可以以 任意顺序 返回答案
1 <= s.length <= 10^4
1 <= words.length <= 5000
1 <= words[i].length <= 30
words[i] 和 s 由小写英文字母组成
测试链接 : https://leetcode.cn/problems/substring-with-concatenation-of-all-words/
小扣在探索丛林的过程中,无意间发现了传说中"落寞的黄金之都"
而在这片建筑废墟的地带中,小扣使用探测仪监测到了存在某种带有「祝福」效果的力场
经过不断的勘测记录,小扣将所有力场的分布都记录了下来
forceField[i] = [x,y,side]
表示第 i 片力场将覆盖以坐标 (x,y) 为中心,边长为 side 的正方形区域。
若任意一点的 力场强度 等于覆盖该点的力场数量
请求出在这片地带中 力场强度 最强处的 力场强度
注意:力场范围的边缘同样被力场覆盖。
测试链接 : https://leetcode.cn/problems/xepqZ5/
来自网易
题目出处 : https://leetcode.cn/circle/discuss/uOnnUA/
已知一个n*n的01矩阵
只能通过通过行交换、或者列交换的方式调整矩阵,
判断这个矩阵的对角线是否能全为1如果能返回true不能返回false
我们升级一下:
已知一个n*n的01矩阵
只能通过通过行交换、或者列交换的方式调整矩阵,
判断这个矩阵的对角线是否能全为1如果不能打印-1
如果能,打印需要交换的次数,并且打印怎么交换
测试链接 : http://acm.hdu.edu.cn/showproblem.php?pid=2819

Loading…
Cancel
Save