今天就做了1,2,3,感觉还是不太理解二分的思想,碰到这种二分想的就是暴力做了,先放着吧,把动态规划刷完回来看二分怎么做
带分数(全排列)
题目链接:https://www.lanqiao.cn/problems/208/learning/
这个题首先分析一下它的算式,需要的结果是需要找到a,b,c,使得对于给定的N,有 N = a + b / c N=a+b/c N=a+b/c成立,并且a,b,c中每一位数字都不相同,这样就可以变成一个全排列问题,我们首先需要得到九位数的所有排列情况,然后在排列情况中再进行按不同的划分方式划分为3个不同数字,然后去判断是否符合上面给定的式子。
首先全排列的话可以使用dfs直接得到全排列的式子,然后在找到一种全排列的情况后对当前的排列进行划分,按照顺序划分为3个数字,然后判断等式是否成立,如果成立,则答案加一,否则直接遍历下一种情况
package daily;
import java.util.Scanner;
/**
* https://www.lanqiao.cn/problems/208/learning/
*
* @author Jia
*
*/
public class day3_21_1 {
static int ans = 0;
static int N;
static int[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
N = sc.nextInt();
sc.close();
backtrack(0);
System.out.println(ans);
}
/**
* dfs遍历得到所有的排列情况
*
* @param index
*/
private static void backtrack(int index) {
if (index == arr.length) {
// 找到了一种排列方式
check();
} else {
for (int i = index; i < arr.length; i++) {
swap(i, index);
backtrack(index + 1);
swap(i, index);
}
}
}
/**
* 判断在当前这种排列情况下是否可以找到满足条件的a,b,c
*/
private static void check() {
// N = a + b/c 转化为判断 N*c == a*c + b
for (int i = 0; i <= 6; i++) {
for (int j = i + 1; j <= 7; j++) {
int a = getVal(0, i);
int b = getVal(i + 1, j);
int c = getVal(j + 1, 8);
if (N * c == a * c + b) {
ans++;
}
}
}
}
/**
* 获得数组指定区间中拼接的数字,范围是:[i,j]
*
* @param i
* @param j
* @return
*/
private static int getVal(int i, int j) {
int count = 0;
for (int k = i; k <= j; k++) {
count = count * 10 + arr[k];
}
return count;
}
private static void swap(int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
走迷宫(bfs)
题目链接:https://www.lanqiao.cn/problems/1216/learning/
这个题也比较常规,前面已经见过好多了,直接bfs遍历,然后统计走了几步就行了
和前面出现的非常类似,可以直接当做模板背过了,解析的话参考这个题迷宫,这里就不重复写了
唯一要注意的点就是这个题里面的最后一行的输入的坐标是从1开始的,对应到矩阵里面需要 - 1 然后才对应实际的坐标
package daily;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.Deque;
import java.util.LinkedList;
/**
* https://www.lanqiao.cn/problems/1216/learning/
*
* @author Jia
*
*/
public class day3_21_2 {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static StreamTokenizer in = new StreamTokenizer(br);
/**
* java快读模板
*
* @return
* @throws IOException
*/
static int nextInt() throws IOException {
in.nextToken();
return (int) in.nval;
}
public static void main(String[] args) throws IOException {
int N = nextInt();
int M = nextInt();
int[][] maze = new int[N][M];
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
maze[i][j] = nextInt();
}
}
boolean[][] meet = new boolean[N][M];// 标记是否看过这个这字,防止重复遍历
MazeNode begin = new MazeNode(nextInt() - 1, nextInt() - 1);// 开始节点
MazeNode end = new MazeNode(nextInt() - 1, nextInt() - 1);// 结束节点
int ans = 0;
Deque<MazeNode> deque = new LinkedList<>();
deque.addLast(begin);
meet[begin.row][begin.col] = true;
int[][] direction = { { 1, 0 }, { 0, -1 }, { -1, 0 }, { 0, 1 } };// 4个方向
boolean flag = false;
while (!deque.isEmpty()) {
int size = deque.size();
ans++;
while (size > 0) {
size--;
MazeNode node = deque.removeFirst();
for (int i = 0; i < direction.length; i++) {
// 遍历四个方向
int newRow = node.row + direction[i][1];
int newCol = node.col + direction[i][0];
// 判断是不是在界内并且这个格子有路且没有看过
if (newRow >= 0 && newRow < N && newCol >= 0 && newCol < M && maze[newRow][newCol] == 1
&& meet[newRow][newCol] == false) {
MazeNode newNode = new MazeNode(newRow, newCol);
meet[newRow][newCol] = true;// 标记为已经看过了
deque.addLast(newNode);
if (end.equals(newNode)) {
// 表示找到了终止节点直接退出循环
flag = true;
break;
}
}
}
if (flag) {
break;
}
}
if (flag) {
break;
}
}
System.out.println(flag ? ans : -1);
}
}
// 节点类
class MazeNode {
int row;
int col;
public MazeNode(int row, int col) {
super();
this.row = row;
this.col = col;
}
public boolean equals(MazeNode obj) {
return row == obj.row && col == obj.col;
}
}
蓝桥幼儿园(并查集)
题目链接:https://www.lanqiao.cn/problems/1135/learning/
这个题用到了并查集的知识,我也不是很会,也是现学直接用的,这玩意也就是一个模板可以直接背过了用就行,我以为有现成的API,找了找发现没有,然后就看博客去啃了,参考的是这篇博客,感觉很清楚,如果不会的话可以去看看
实现并查集常用的比较简单的有两种方法,一种是不带路径压缩(左图),一种带路径压缩(右图),说白了讲就是要不要把原来节点的老大也换掉(不明白去看看上面那个博客),如果换掉的话查找的时间肯定会缩短,对于比较大的数据集可以大大减少查找时间
这个题本身也不是很难,主要是对并查集基本操作的考察,希望可以记下,最好是直接背过
package daily;
import java.util.Scanner;
public class day3_21_3 {
static int[] searchSet;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int N = sc.nextInt();
int M = sc.nextInt();
searchSet = new int[N + 1];
init(searchSet);
for (int i = 0; i < M; i++) {
int op = sc.nextInt();
int x = sc.nextInt();
int y = sc.nextInt();
if (op == 1) {
// 交朋友
join(x, y);
} else {
// 判断是否是朋友
int xParent = find_compact(x);
int yParent = find_compact(y);
System.out.println(xParent == yParent ? "YES" : "NO");
}
}
sc.close();
}
/**
* 将x,y所在的集合合并
*
* @param x
* @param y
*/
private static void join(int x, int y) {
int xParent = find_compact(x);
int yParent = find_compact(y);
if (xParent < yParent) {
searchSet[yParent] = xParent;
} else {
searchSet[xParent] = yParent;
}
}
/**
* 判断两个数是否在同一个集合中, 不带路径压缩
*
* @param x
*/
private static int find(int x) {
return searchSet[x] == x ? x : find(searchSet[x]);
}
/**
* 判断两个数是否在同一个集合中, 带路径压缩
*
* @param x
*/
private static int find_compact(int x) {
return searchSet[x] == x ? x : (searchSet[x] = find_compact(searchSet[x]));
}
/**
* 设置每个人和自己在一个集合中
*
* @param child
*/
private static void init(int[] child) {
for (int i = 0; i < child.length; i++) {
child[i] = i;
}
}
}