modify code

master
algorithmzuo 3 years ago
parent d4e75a11e8
commit e856d967cb

@ -0,0 +1,199 @@
package class_2022_08_2_week;
// 来自猿辅导
// 2022.8.7笔试第三道
// 给定一个数组arr和一个正数k
// 如果arr[i] == 0表示i这里既可以是左括号也可以是右括号
// 而且可以涂上1~k每一种颜色
// 如果arr[i] != 0表示i这里已经确定是左括号颜色就是arr[i]的值
// 那么arr整体就可以变成某个括号字符串并且每个括号字符都带有颜色
// 返回在括号字符串合法的前提下,有多少种不同的染色方案
// 不管是排列、还是颜色,括号字符串任何一点不一样,就算不同的染色方案
// 最后的结果%10001为了方便我们不处理mod就管核心思路
// 2 <= arr长度 <= 5000
// 1 <= k <= 1000
// 0 <= arr[i] <= k
public class Code01_ParenthesesDye {
// 暴力方法
// 为了验证
public static int ways1(int[] arr, int k) {
if ((arr.length & 1) != 0) {
return 0;
}
return process1(arr, 0, k);
}
public static int process1(int[] arr, int index, int k) {
if (index == arr.length) {
int n = arr.length;
int[] stack = new int[n];
int size = 0;
for (int i = 0; i < n; i++) {
if (arr[i] > 0) {
stack[size++] = arr[i];
} else {
if (size == 0 || stack[--size] != -arr[i]) {
return 0;
}
}
}
return size == 0 ? 1 : 0;
} else if (arr[index] != 0) {
return process1(arr, index + 1, k);
} else {
int ans = 0;
for (int color = 1; color <= k; color++) {
arr[index] = color;
ans += process1(arr, index + 1, k);
arr[index] = -color;
ans += process1(arr, index + 1, k);
arr[index] = 0;
}
return ans;
}
}
// 正式方法
// 时间复杂度O(N^2), N是数组长度
// 首先求合法的括号组合数量(忽略染色这件事),
// 就是combines方法看注释
// 当括号数量求出来,再看染色能有几种
// 比如忽略颜色,某个合法的括号结合 长度为n
// 如果已经有b个涂上了颜色而且是左括号
// 那么,因为该结合是合法的,
// 所以这b个涂上了颜色的左括号和哪些右括号结合
// 其实是确定的,这些右括号颜色也是确定的
// 那么还剩n-(b*2)个字符
// 这n-(b*2)个字符,就是(n-(b*2))/2对括号
// 每对括号都可以自由发挥所以任何一个合法的组合涂色方案为k^((n-(b*2))/2)
// 最终答案 : 合法括号组合数量 * k^((n-(b*2))/2)
public static int ways2(int[] arr, int k) {
int n = arr.length;
if ((n & 1) != 0) {
return 0;
}
int a = combines(arr);
int b = 0;
for (int num : arr) {
if (num != 0) {
b++;
}
}
return a * ((int) Math.pow((double) k, (double) ((n - (b << 1)) >> 1)));
}
// 忽略染色这件事,求合法的括号结合数量
public static int combines(int[] arr) {
int n = arr.length;
int[][] dp = new int[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
dp[i][j] = -1;
}
}
return f(arr, 0, 0, dp);
}
// arr[i....]范围上,去做决定
// j : arr[0..i-1]已经做完决定的部分,左括号比右括号,多几个
// 返回:
// arr[i....]范围上,去做决定,
// 已经做完决定的部分左括号比右括号多j个
// 这样的情况下,最终合法的括号结合,多少个!
// process(arr, 0, 0)
public static int process(int[] arr, int i, int j) {
if (i == arr.length) {
return j == 0 ? 1 : 0;
}
if (j < 0) {
return 0;
}
// 这个不写也行
// 锦上添花的剪枝条件
if (arr.length - i < j) {
return 0;
}
// arr[i] != 0
if (arr[i] != 0) {
// (
return process(arr, i + 1, j + 1);
} else {
// arr[i] 0 ? ( )
int p1 = process(arr, i + 1, j + 1);
int p2 = process(arr, i + 1, j - 1);
return p1 + p2;
}
}
// 在arr[i...]范围上做决定
// 之前在arr[0...i-1]上的决定使得左括号比右括号多了j个
// 最终合法的括号结合是多少
public static int f(int[] arr, int i, int j, int[][] dp) {
int n = arr.length;
if (i == n) {
return j == 0 ? 1 : 0;
}
if (j < 0) {
return 0;
}
if (n - i < j) {
return 0;
}
// 如果缓存命中,直接返回答案
if (dp[i][j] != -1) {
return dp[i][j];
}
int ans = 0;
if (arr[i] > 0) {
ans = f(arr, i + 1, j + 1, dp);
} else {
ans = f(arr, i + 1, j + 1, dp) + f(arr, i + 1, j - 1, dp);
}
dp[i][j] = ans;
return ans;
}
// 生成长度随机的数组
// 值在0~K之间但是50%的概率值是050%的概率值是1~k中的一个
public static int[] randomArray(int n, int k) {
int[] ans = new int[n];
for (int i = 0; i < n; i++) {
ans[i] = Math.random() < 0.5 ? 0 : ((int) (Math.random() * k) + 1);
}
return ans;
}
public static void main(String[] args) {
int N = 5;
int K = 4;
int testTimes = 1000;
System.out.println("功能测试开始");
for (int i = 0; i < testTimes; i++) {
int n = ((int) (Math.random() * N) + 1) << 1;
int k = (int) (Math.random() * K) + 1;
int[] arr = randomArray(n, k);
int ans1 = ways1(arr, k);
int ans2 = ways2(arr, k);
if (ans1 != ans2) {
System.out.println("出错了!");
}
}
System.out.println("功能测试结束");
System.out.println("性能测试开始");
int n = 5000;
int k = 1000;
System.out.println("数组长度 : " + n);
System.out.println("颜色数量 : " + k);
int[] arr = randomArray(n, k);
long start = System.currentTimeMillis();
ways2(arr, k);
long end = System.currentTimeMillis();
System.out.println("运行时间 : " + (end - start) + "毫秒");
System.out.println("性能测试结束");
System.out.println("注意 : 这个解答没有取mod只展示了核心思路");
}
}

@ -0,0 +1,161 @@
package class_2022_08_2_week;
import java.util.ArrayList;
import java.util.Arrays;
// 来自米哈游
// 给定一个正数n表示有多少个节点
// 给定一个二维数组edges表示所有无向边
// edges[i] = {a, b} 表示a到b有一条无向边
// edges一定表示的是一个无环无向图也就是树结构
// 每个节点可以染1、2、3三种颜色
// 要求 : 非叶节点的相邻点一定要至少有两种和自己不同颜色的点
// 返回一种达标的染色方案,也就是一个数组,表示每个节点的染色状况
// 1 <= 节点数量 <= 10的5次方
public class Code02_TreeDye {
// 1 2 3 1 2 3 1 2 3
public static int[] rule1 = { 1, 2, 3 };
// 1 3 2 1 3 2 1 3 2
public static int[] rule2 = { 1, 3, 2 };
public static int[] dye(int n, int[][] edges) {
ArrayList<ArrayList<Integer>> graph = new ArrayList<>();
// 0 : { 2, 1 }
// 1 : { 0 }
// 2 : { 0 }
for (int i = 0; i < n; i++) {
graph.add(new ArrayList<>());
}
for (int[] edge : edges) {
// 0 -> 2
// 1 -> 0
graph.get(edge[0]).add(edge[1]);
graph.get(edge[1]).add(edge[0]);
}
// 选一个头节点!
int head = -1;
for (int i = 0; i < n; i++) {
if (graph.get(i).size() >= 2) {
head = i;
break;
}
}
// graph
// head
int[] colors = new int[n];
if (head == -1) { // 两个点,互相连一下
// 把colors所有位置都设置成1
Arrays.fill(colors, 1);
} else {
// dfs 染色了!
colors[head] = 1;
dfs(graph, graph.get(head).get(0), 1, rule1, colors);
for (int i = 1; i < graph.get(head).size(); i++) {
dfs(graph, graph.get(head).get(i), 1, rule2, colors);
}
}
return colors;
}
// 整个图结构都在graph
// 当前来到的节点是head号节点
// head号节点在level层
// 染色的规则rule {1,2,3...} {1,3,2...}
// 做的事情以head为头的整颗树每个节点都染上颜色
// 填入到colors数组里去
public static void dfs(
ArrayList<ArrayList<Integer>> graph,
int head,
int level,
int[] rule, int[] colors) {
colors[head] = rule[level % 3];
for (int next : graph.get(head)) {
if (colors[next] == 0) {
dfs(graph, next, level + 1, rule, colors);
}
}
}
// 为了测试
// 生成无环无向图
public static int[][] randomEdges(int n) {
int[] order = new int[n];
for (int i = 0; i < n; i++) {
order[i] = i;
}
for (int i = n - 1; i >= 0; i--) {
swap(order, i, (int) (Math.random() * (i + 1)));
}
int[][] edges = new int[n - 1][2];
for (int i = 1; i < n; i++) {
edges[i - 1][0] = order[i];
edges[i - 1][1] = order[(int) (Math.random() * i)];
}
return edges;
}
// 为了测试
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
// 为了测试
public static boolean rightAnswer(int n, int[][] edges, int[] colors) {
ArrayList<ArrayList<Integer>> graph = new ArrayList<>();
for (int i = 0; i < n; i++) {
graph.add(new ArrayList<>());
}
for (int[] edge : edges) {
graph.get(edge[0]).add(edge[1]);
graph.get(edge[1]).add(edge[0]);
}
boolean[] hasColors = new boolean[4];
for (int i = 0, colorCnt = 1; i < n; i++, colorCnt = 1) {
if (colors[i] == 0) {
return false;
}
if (graph.get(i).size() <= 1) { // i号点是叶节点
continue;
}
hasColors[colors[i]] = true;
for (int near : graph.get(i)) {
if (!hasColors[colors[near]]) {
hasColors[colors[near]] = true;
colorCnt++;
}
}
if (colorCnt != 3) {
return false;
}
Arrays.fill(hasColors, false);
}
return true;
}
public static void main(String[] args) {
// int n = 7;
// int[][] edges = randomEdges(n);
// for (int[] edge : edges) {
// System.out.println(edge[0] + " , " + edge[1]);
// }
int N = 100;
int testTimes = 1000;
System.out.println("测试开始");
for (int i = 0; i < testTimes; i++) {
int n = (int) (Math.random() * N) + 1;
int[][] edges = randomEdges(n);
int[] ans = dye(n, edges);
if (!rightAnswer(n, edges, ans)) {
System.out.println("出错了");
}
}
System.out.println("测试结束");
}
}

@ -0,0 +1,89 @@
package class_2022_08_2_week;
import java.util.Stack;
// 给定一个逆波兰式
// 转化成正确的中序表达式
// 要求只有必要加括号的地方才加括号
public class Code03_ReversePolishNotation {
// 请保证给定的逆波兰式是正确的!
public static int getAns(String rpn) {
if (rpn == null || rpn.equals("")) {
return 0;
}
String[] parts = rpn.split(" ");
Stack<Integer> stack = new Stack<>();
for (String part : parts) {
if (part.equals("+") || part.equals("-") || part.equals("*") || part.equals("/")) {
int right = stack.pop();
int left = stack.pop();
int ans = 0;
if (part.equals("+")) {
ans = left + right;
} else if (part.equals("-")) {
ans = left - right;
} else if (part.equals("*")) {
ans = left * right;
} else {
ans = left / right;
}
stack.push(ans);
} else {
stack.push(Integer.valueOf(part));
}
}
// stack 只有一个数,最终的结果
return stack.pop();
}
enum Operation {
SingleNumber, AddOrMinus, MultiplyOrDivide;
}
// 请保证输入的逆波兰式是正确的
// 否则该函数不保证正确性
// 逆波兰式仅支持+、-、*、/
// 想支持更多算术运算符自己改
public static String convert(String rpn) {
if (rpn == null || rpn.equals("")) {
return rpn;
}
String[] parts = rpn.split(" ");
Stack<String> stack1 = new Stack<>();
Stack<Operation> stack2 = new Stack<>();
for (String cur : parts) {
// cur 当前遇到的字符串
// +- */ 单数
if (cur.equals("+") || cur.equals("-")) {
String b = stack1.pop();
String a = stack1.pop();
stack2.pop();
stack2.pop();
stack1.push(a + cur + b);
stack2.push(Operation.AddOrMinus);
} else if (cur.equals("*") || cur.equals("/")) {
String b = stack1.pop();
String a = stack1.pop();
Operation bOp = stack2.pop();
Operation aOp = stack2.pop();
String left = aOp == Operation.AddOrMinus ? ("(" + a + ")") : (a);
String right = bOp == Operation.AddOrMinus ? ("(" + b + ")") : (b);
stack1.push(left + cur + right);
stack2.push(Operation.MultiplyOrDivide);
} else {
stack1.push(cur);
stack2.push(Operation.SingleNumber);
}
}
return stack1.pop();
}
public static void main(String[] args) {
// 3*(-5+13)+6/(2-3+2)-4*5*3
String rpn = "3 -5 13 + * 6 2 3 - 2 + / + 4 5 3 * * -";
System.out.println(getAns(rpn));
System.out.println(convert(rpn));
}
}

@ -0,0 +1,92 @@
package class_2022_08_2_week;
// 给定平面上n个点x和y坐标都是整数
// 找出其中的一对点的距离使得在这n个点的所有点对中该距离为所有点对中最小的
// 返回最短距离精确到小数点后面4位
// 测试链接 : https://www.luogu.com.cn/problem/P1429
// 提交如下代码把主类名改成Main可以直接通过
// T(N) = 2*T(N/2) + O(N*logN)
// 这个表达式的时间复杂度是O(N*(logN的平方))
// 复杂度证明 : https://math.stackexchange.com/questions/159720/
// 网上大部分的帖子,答案都是这个复杂度
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_ClosestTwoPoints1 {
public static int N = 200001;
public static Point[] points = new Point[N];
public static Point[] deals = new Point[N];
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 n = (int) in.nval;
for (int i = 0; i < n; i++) {
in.nextToken();
double x = (double) in.nval;
in.nextToken();
double y = (double) in.nval;
points[i] = new Point(x, y);
}
Arrays.sort(points, 0, n, (a, b) -> a.x <= b.x ? -1 : 1);
double ans = nearest(0, n - 1);
out.println(String.format("%.4f", ans));
out.flush();
}
}
public static class Point {
public double x;
public double y;
public Point(double a, double b) {
x = a;
y = b;
}
}
public static double nearest(int left, int right) {
double ans = Double.MAX_VALUE;
if (left == right) {
return ans;
}
int mid = (right + left) / 2;
ans = Math.min(nearest(left, mid), nearest(mid + 1, right));
int l = mid;
int r = mid + 1;
int size = 0;
while (l >= left && points[mid].x - points[l].x <= ans) {
deals[size++] = points[l--];
}
while (r <= right && points[r].x - points[mid].x <= ans) {
deals[size++] = points[r++];
}
Arrays.sort(deals, 0, size, (a, b) -> a.y <= b.y ? -1 : 1);
for (int i = 0; i < size; i++) {
for (int j = i + 1; j < size; j++) {
if (deals[j].y - deals[i].y >= ans) {
break;
}
ans = Math.min(ans, distance(deals[i], deals[j]));
}
}
return ans;
}
public static double distance(Point a, Point b) {
double x = a.x - b.x;
double y = a.y - b.y;
return Math.sqrt(x * x + y * y);
}
}

@ -0,0 +1,114 @@
package class_2022_08_2_week;
// 给定平面上n个点x和y坐标都是整数
// 找出其中的一对点的距离使得在这n个点的所有点对中该距离为所有点对中最小的
// 返回最短距离精确到小数点后面4位
// 测试链接 : https://www.luogu.com.cn/problem/P1429
// 提交如下代码把主类名改成Main可以直接通过
// T(N) = 2*T(N/2) + O(N)
// 这个表达式我们很熟悉,和归并排序一样的表达式
// 时间复杂度是O(N*logN)
// 需要用到归并排序的技巧才能做到
// 我们课上的独家
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_ClosestTwoPoints2 {
public static int N = 200001;
public static Point[] points = new Point[N];
public static Point[] merge = new Point[N];
public static Point[] deals = new Point[N];
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 n = (int) in.nval;
for (int i = 0; i < n; i++) {
in.nextToken();
double x = (double) in.nval;
in.nextToken();
double y = (double) in.nval;
points[i] = new Point(x, y);
}
Arrays.sort(points, 0, n, (a, b) -> a.x <= b.x ? -1 : 1);
double ans = nearest(0, n - 1);
out.println(String.format("%.4f", ans));
out.flush();
}
}
public static class Point {
public double x;
public double y;
public Point(double a, double b) {
x = a;
y = b;
}
}
public static double nearest(int left, int right) {
double ans = Double.MAX_VALUE;
if (left == right) {
return ans;
}
int mid = (right + left) / 2;
double midX = points[mid].x;
ans = Math.min(nearest(left, mid), nearest(mid + 1, right));
int p1 = left;
int p2 = mid + 1;
int mergeSize = left;
int dealSize = 0;
while (p1 <= mid && p2 <= right) {
merge[mergeSize] = points[p1].y <= points[p2].y ? points[p1++] : points[p2++];
if (Math.abs(merge[mergeSize].x - midX) <= ans) {
deals[dealSize++] = merge[mergeSize];
}
mergeSize++;
}
while (p1 <= mid) {
merge[mergeSize] = points[p1++];
if (Math.abs(merge[mergeSize].x - midX) <= ans) {
deals[dealSize++] = merge[mergeSize];
}
mergeSize++;
}
while (p2 <= right) {
merge[mergeSize] = points[p2++];
if (Math.abs(merge[mergeSize].x - midX) <= ans) {
deals[dealSize++] = merge[mergeSize];
}
mergeSize++;
}
for (int i = left; i <= right; i++) {
points[i] = merge[i];
}
for (int i = 0; i < dealSize; i++) {
for (int j = i + 1; j < dealSize; j++) {
if (deals[j].y - deals[i].y >= ans) {
break;
}
ans = Math.min(ans, distance(deals[i], deals[j]));
}
}
return ans;
}
public static double distance(Point a, Point b) {
double x = a.x - b.x;
double y = a.y - b.y;
return Math.sqrt(x * x + y * y);
}
}

@ -1536,6 +1536,41 @@ int query(int left, int right, int threshold) 
第035节 2022年8月第2周流行算法题目解析
来自猿辅导
2022.8.7笔试第三道
给定一个数组arr和一个正数k
如果arr[i] == 0表示i这里既可以是左括号也可以是右括号
而且可以涂上1~k每一种颜色
如果arr[i] != 0表示i这里已经确定是左括号颜色就是arr[i]的值
那么arr整体就可以变成某个括号字符串并且每个括号字符都带有颜色
返回在括号字符串合法的前提下,有多少种不同的染色方案
不管是排列、还是颜色,括号字符串任何一点不一样,就算不同的染色方案
最后的结果%10001为了方便我们不处理mod就管核心思路
2 <= arr长度 <= 5000
1 <= k <= 1000
0 <= arr[i] <= k
来自米哈游
给定一个正数n表示有多少个节点
给定一个二维数组edges表示所有无向边
edges[i] = {a, b} 表示a到b有一条无向边
edges一定表示的是一个无环无向图也就是树结构
每个节点可以染1、2、3三种颜色
要求 : 非叶节点的相邻点一定要至少有两种和自己不同颜色的点
返回一种达标的染色方案,也就是一个数组,表示每个节点的染色状况
1 <= 节点数量 <= 10的5次方
给定一个逆波兰式
转化成正确的中序表达式
要求只有必要加括号的地方才加括号
给定平面上n个点x和y坐标都是整数
找出其中的一对点的距离使得在这n个点的所有点对中该距离为所有点对中最小的
返回最短距离精确到小数点后面4位
测试链接 : https://www.luogu.com.cn/problem/P1429

Loading…
Cancel
Save