文章目录
- 1. 华为 喷墨水 (模拟)
- 2. 华为 表达式计算 (模拟)
- 3. 华为 魔幻深林救公主 (BFS最短路)
- 4. 华为 网格最小可达跳数之和 (BFS最短路)
- 5. 华为 最佳检测顺序(DFS+自定义排序)
- 6. 美团 塔子哥抓敌人 (二维前缀和)
- 7. 用友 滑动窗口最大值 (单调队列)
- 8. 美团 拆分树(DFS) **
- 9. 阿里 联通块 (并查集) **
- 10. 阿里 数组重排 (贪心)
- 11.美团 必经之路(树上DFS) **
- 12. 米哈游 RGB矩阵(DFS岛屿数量进阶版)
- 13. 华为od 开心消消乐 (BFS)
- 14. 华为 跑步(BFS) **
- 15. 美团 RGB树(树形dp)
- 16. 阿里巴巴 满二叉树的数量(记忆化搜索)
- 17. 美团 南北对决 (前缀和)
- 18. 美团 染色的树 (DFS)
- 19. 百度 排列数量 (技巧)
- 20. 腾讯音乐 价值二叉树 (树形DFS)
1. 华为 喷墨水 (模拟)
模拟题,塔子哥提交ac
- 输入处理:双字节十六进制转二进制
十六进制转十进制再转二进制,读取多个字符串多次处理,给数组arr赋值,长度不足就数组首部即字符末尾用0填充 - 核心逻辑:分向右,向左移动分别处理
for循环遍历数组arr,从0开始每次向右/左移动i个位置,检查是否满足条件,满足返回此时移动的步数; - check函数逻辑:
判断不移动和移动了之后,对应arr数组的下标是否为1,为1的话说明此处可以喷墨,步长step+1,最终判断step是否等于数组长度,若是返回true - 输出处理
判断左/右移动标记是否为-1,若不是-1,就返回更新后的数组
package ZhenTi;
import java.sql.SQLOutput;
import java.util.Scanner;
/**
* 5/6喷墨水
*/
public class Solution29 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
sc.nextLine();
String[] strs = sc.nextLine().split(" ");
int[] arr = new int[n];
for(int i=0,c=0; i<strs.length && c<n; i++){
//十六进制变十进制变二进制,前两位0x为前缀
char[] bs = Integer.toBinaryString(Integer.parseUnsignedInt(strs[i].substring(2),16))
.toCharArray();
//长度不足16,后边的用0填充
for(int j=0;j<16-bs.length&&c<n;j++){
arr[c++] = 0;
}
for(int j=0;j<bs.length&&c<n;j++){
arr[c++] = bs[j]- '0';
}
}
// System.out.println(arr);
int ans,ans1=-1,ans2=-1;
//检查向右移动,从0开始遍历,只求最短的,找到了就返回
for(int i=0;i<n;i++){
if(check(arr,i,0)){
ans1 = i;
break;
}
}
//检查向左移动
for(int i=0;i<n;i++){
if(check(arr,i,1)){
ans2 = i;
break;
}
}
ans = (ans1==-1?0:1) + (ans2==-1?0:1);
System.out.println(ans);
if(ans1!=-1){
System.out.println("+" + ans1);
int[] res = new int[n];
for(int i=0;i<n;i++){
if(arr[i]==0){
//输出要求堵塞的孔开启即可,因此先找到堵塞的孔处理
if(arr[i]==0){
res[i-ans1] = 1;
}
}
}
for(int item:res){
System.out.print(item);
}
System.out.println();
}
if(ans2!=-1){
System.out.println("-" + ans2);
int[] res = new int[n];
for(int i=0;i<n;i++){
if(arr[i]==0){
//输出要求堵塞的孔开启即可,因此先找到堵塞的孔处理
if(arr[i]==0){
res[i+ans2] = 1;
}
}
}
for(int item:res){
System.out.print(item);
}
}
}
//cnt表示位移步数,dir表示移动方向,0表示向右移动,1表示向左移动
public static boolean check(int[] arr,int cnt,int dir){
int ans = 0;
int n = arr.length;
if(dir==0){//向右移动
for(int i=0,j;i<n;i++){
//两种情况:不移动可以实现喷墨或者向右移动cnt位也能实现喷墨
if(arr[i]==1 || ((j=i-cnt)>=0&&arr[j]==1 )){
ans++;
}else{
break;
}
}
}else{//向左移动
for(int i=0,j;i<n;i++){
//两种情况:不移动可以实现喷墨或者向左移动cnt位也能实现喷墨
if(arr[i]==1 || ((j=i+cnt)<n&&arr[j]==1 )){
ans++;
}else{
break;
}
}
}
return ans == n;//如果ans等于数组长度n就说明喷墨成功
}
}
2. 华为 表达式计算 (模拟)
复杂的模拟题,塔子哥提交AC
分成数字和特殊字符两部分,转换成数字相加,就不需要考虑进位的问题了
首先,遇到特殊字符就将该位换成0,并记录下特殊字符和位置,最后得到数字部分的值相加。 然后计算特殊字符相加的值,也就是题目中表达式的值乘上10的(所在位置相对小数点的位置)幂次方,比如小数点后一位就乘上10的-1幂次方。 最后转字符串处理并输出。
package ZhenTi;
/**
* 5/6 模拟加法 华为
*/
import java.math.BigDecimal;
import java.util.*;
public class Solution30{
static HashMap<String,Double> sp = new HashMap<>();//特殊字符的加法原则
static StringBuilder spl = new StringBuilder();//记录所有的特殊字符
static BigDecimal cnt = BigDecimal.valueOf(0.0);//记录处理特殊字符后的数字
static int count = 0;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
in.nextLine();
String[] str = in.nextLine().split("\\+");
String left = str[0]; //第一个数
String right = str[1]; //第二个数
HashMap<Integer,Integer> leftSpecial = divide(left);//个数(从0开始):索引
HashMap<Integer,Integer> rightSpecial = divide(right);//得到cnt 用0替代特殊字符后,两个数字之和
//加法规则
sp.put("!+!",0.0);
sp.put("!+@",13.0);sp.put("@+!",13.0);
sp.put("!+#",4.0);sp.put("#+!",4.0);
sp.put("@+@",7.0);
sp.put("@+#",20.0);sp.put("#+@",20.0);
sp.put("#+#",5.0);
//处理特殊字符
int l = spl.length() / 2;//第一个数字的特殊字符的位置和第二数字对应特殊字符位置相差len/2
int index = 0;
for (int i = 0; i < l; i++) {
String temp = spl.charAt(i) + "+" + spl.charAt(l + i);
int idx = left.indexOf('.');//判断是否有小数点
if(idx == -1){//没有小数点, 最终结果为 sum*10^index
index = left.length() - leftSpecial.get(i) - 1;
}else{//有小数点,如果特殊字符在整数部分sum*10^index;小数部分,sum*10^-index
int tem = leftSpecial.get(i);
if(tem < idx){
index = idx - tem - 1;
}else{
index = -(tem - idx);
}
}
Double num = sp.get(temp) * Math.pow(10,index);
String tempnum = String.valueOf(num);
BigDecimal bigtemp = new BigDecimal(tempnum);
cnt = cnt.add(bigtemp);//特殊字符的求和结果
}
//处理结果:去除后置0和小数点
StringBuilder res = new StringBuilder(cnt.toString());
int length = res.length();
while(res.charAt(length -1) == '0'){
res.deleteCharAt(length - 1);
length--;
}
if(res.charAt(length - 1) == '.'){
res.deleteCharAt(length - 1);
}
System.out.println(res);
}
//处理数字:把特殊字符变为0
public static HashMap<Integer,Integer> divide(String str){
int l = str.length();
//存在小数点,返回索引;否则返回-1
int index = str.indexOf(".");
//保存特殊字符索引
HashMap<Integer,Integer> Special = new HashMap<>();//count特殊字符个数-1 : i在字母中对应索引
StringBuilder res = new StringBuilder();
if(index == -1){ //无小数点
for (int i = 0; i < l; i++) { //特殊字符用0替代
if(str.charAt(i) < '0' || str.charAt(i) > '9'){
Special.put(count,i);//放入哈希表
count++;//特殊字符统计个数+1
spl.append(str.charAt(i));//记录特殊字符
res.append('0');//用0代替
}else{//普通字符直接添加
res.append(str.charAt(i));
}
}
}else{ //有小数点,以小数点为间隔做相同的处理 中间加上小数点即可
for (int i = 0; i < index; i++) {
if(str.charAt(i) < '0' || str.charAt(i) > '9'){
//特殊字符;
Special.put(count,i);
count++;
spl.append(str.charAt(i));
res.append('0');
}else{
res.append(str.charAt(i));
}
}
res.append('.');
for (int i = index + 1; i < l; i++) {
//有小数点
if(str.charAt(i) < '0' || str.charAt(i) > '9'){
//特殊字符;
Special.put(count,i);
count++;
spl.append(str.charAt(i));
res.append('0');
}else{
res.append(str.charAt(i));
}
}
}
BigDecimal bigres = new BigDecimal(res.toString());
cnt = cnt.add(bigres);//记录处理特殊字符后的数字,相加,得到数字1和数字2的和
return Special;
}
}
3. 华为 魔幻深林救公主 (BFS最短路)
最短路模型
队列元素:x坐标,y坐标,时间轮次
每一个轮次 遍历四个方向 + 停留在原地
package ZhenTi;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.Scanner;
/**
* 5/6华为第三题:找公主
*/
public class Solution31 {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
//大小,怪物数量
int n = scan.nextInt(), k = scan.nextInt();
boolean[][] traps = new boolean[n][n];//怪物位置
for (int i = 0; i < k; i++) {
traps[scan.nextInt()][scan.nextInt()] = true;
}
//公主和王子的位置
int gx = scan.nextInt(), gy = scan.nextInt(), wx = scan.nextInt(), wy = scan.nextInt();
String[][] strings = new String[n][];
scan.nextLine();
for (int i = 0; i < n; i++) {
strings[i] = scan.nextLine().split(" ");
}
char[][][] grid = new char[n][n][3];//地图表格
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
grid[i][j] = strings[i][j].toCharArray();
}
}
boolean[][][] vis = new boolean[n][n][3];
int[][] dirs = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
Queue<int[]> queue = new ArrayDeque<>();
queue.offer(new int[]{wx, wy, 0});//两个坐标+时间轮次
int ans = 0;
while (!queue.isEmpty()) {
for (int i = queue.size(); i > 0; i--) {
int[] cur = queue.poll();
if (cur[0] == gx && cur[1] == gy) {//找到了公主
System.out.println(ans);
return;
}
int turn = (cur[2] + 1) % 3;//计算当前轮次
for (int[] dir : dirs) {//四个方向搜索
int x = cur[0] + dir[0], y = cur[1] + dir[1];
//边界,是否有怪物,障碍,是否访问过
if (x < 0 || x >= n || y < 0 || y >= n || traps[x][y] || grid[x][y][turn] == '1' || vis[x][y][turn]) continue;
queue.offer(new int[]{x, y, turn});
vis[x][y][turn] = true;
}
//可以选择停留在原地,当下一个轮次没有障碍物且未访问过
if (grid[cur[0]][cur[1]][turn] != '1' && !vis[cur[0]][cur[1]][turn]) {
queue.offer(new int[]{cur[0], cur[1], turn});
vis[cur[0]][cur[1]][turn] = true;
}
}
ans++;//步长
}
System.out.println(-1);
}
}
4. 华为 网格最小可达跳数之和 (BFS最短路)
package ZhenTi;
/**
* 网络最小可达跳数之和
* 多源最短路径BFS
* 参考leetcodeT417. 太平洋大西洋水流问题
*/
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class Solution6 {
static int[][] dirs = {{-1,0},{1,0},{0,-1},{0,1}};
static boolean[][] visited;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
String[][] str = new String[n][n];
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
str[i][j] = sc.next();
}
}
visited = new boolean[n][n];
//开始处理:从目标点反向搜索
Queue<int[]> que = new LinkedList<>();
int[][] nums = new int[n][n];//每个点到达终点的步数,BC默认0
int res = 0;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if (str[i][j].equals("C")){
que.offer(new int[]{i,j});
}
}
}
while(!que.isEmpty()){
int[] temp = new int[2];
temp[0] = que.peek()[0];
temp[1] = que.poll()[1];
for(int[] dir:dirs){
int newX = temp[0] + dir[0];
int newY = temp[1] + dir[1];
if(newX<0||newY<0||newX>=n||newY>=n||visited[newX][newY]|| !str[newX][newY].equals("A")){//边界条件+访问过+非A
continue;
}
que.offer(new int[]{newX,newY});
visited[newX][newY] = true;
nums[newX][newY] = nums[temp[0]][temp[1]]+1;
res += nums[newX][newY];
}
}
System.out.println(res);
}
}
5. 华为 最佳检测顺序(DFS+自定义排序)
构建连接图,dfs找到每个点的前继点的个数
自定义排序,技巧性可以学习
初始化大小不能设置太大,根据题目数据大小设置,一开始多打了个0,一直超时
import java.util.*;
public class Main {
static Map<Integer, List<Integer>> map = new HashMap<>();//节点:依赖该节点的
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
//处理逻辑
//构建每个点的依赖关系
//初始化 数据最多100000
for(int i=0;i<100010;i++){
map.put(i,new ArrayList<>());
}
for(int i=0;i<n;i++){
int temp = sc.nextInt();
if(temp!=-1){
map.get(temp).add(i);//节点:依赖该节点的
}
}
//DFS找到每个节点的依赖点的个数
List<int[]> res = new ArrayList<>();// [a,b]a表示依赖点的个数,b表示节点序号
for(int i=0;i<n;i++){
res.add(new int[]{dfs(i),i});
}
//自定义排序
Collections.sort(res, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
if(o1[0]!=o2[0]){
return o2[0]-o1[0];
}else{
return Integer.compare(o1[1], o2[1]);
}
}
});
for(int[] item:res){
System.out.print(item[1]+" ");
}
}
public static int dfs(int i){
int count = 0;
for(int j:map.get(i)){
count += dfs(j);
}
return count+1;
}
}
6. 美团 塔子哥抓敌人 (二维前缀和)
理论学习:
参考文章
package ZhenTi;
/**
* https://blog.codefun2000.com/company/mt/3-18/P1087.html#%E4%BB%A3%E7%A0%81
*/
import java.util.Scanner;
public class Solution33 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int a = sc.nextInt();
int b = sc.nextInt();
int[][] mp = new int[1011][1011];
for(int i = 0; i < n; i++) {
int x = sc.nextInt();
int y = sc.nextInt();
mp[x][y]++;
}
// 求二维前缀和
//map[i][j] 表示左上角以(0,0),右下角(i,j)的矩形里有多少个敌人
for(int i = 1; i <= 1010; i++) {
for(int j = 1; j <= 1010; j++) {
//二维前缀和公式推导见资料,右上角被重复算了,需要减
mp[i][j] += mp[i-1][j] + mp[i][j-1] - mp[i-1][j-1];
}
}
// 枚举技能矩阵的右下角,并求矩形伤害总和的最大值
int ans = 0;
for(int i = a+1; i <= 1010; i++) {
for(int j = b+1; j <= 1010; j++) {
int t = mp[i][j] - mp[i-a-1][j] - mp[i][j-b-1] + mp[i-a-1][j-b-1];
ans = Math.max(ans, t);
}
}
System.out.println(ans);
}
}
7. 用友 滑动窗口最大值 (单调队列)
leetcode239改版,求两次滑窗,
package ZhenTi;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Scanner;
public class Solution34 {
public static void main(String[] args) {
//为了方便处理下输入
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
String[] sub1 = s.substring(s.indexOf('[')+1,s.indexOf(']')).split(",");
int[] nums = new int[sub1.length];
for(int i=0;i<nums.length;i++){
nums[i] = Integer.parseInt(sub1[i]);
}
String[] sub2 = s.substring((s.indexOf('k'))).split("=");
int k = Integer.parseInt(sub2[sub2.length-1]);
//开始处理
int size = nums.length - k + 1;
int[] resMax = new int[size];
int[] resMin = new int[size];
resMax = slidingWindow(nums,k,true);
resMin = slidingWindow(nums,k,false);
int res = Integer.MIN_VALUE;
for(int i=0;i<size;i++){
res = Math.max(res,resMax[i]-resMin[i]);
}
System.out.println(res);
}
public static int[] slidingWindow(int[] nums, int k,boolean flag) {
if(nums.length == 1) return nums;
int[] res = new int[nums.length-k+1];//所有滑窗里的最大值
MyDeque que = new MyDeque();
for(int i=0;i<k;i++){
que.add(nums[i],flag);
}
res[0] = que.peekMax();
int index = 1;
for(int i=k;i<nums.length;i++){
que.removeFirst(nums[i-k]);//删除滑窗第一个元素(可能不存在)
que.add(nums[i],flag);
res[index++] = que.peekMax();
}
return res;
}
}
class MyDeque{
Deque<Integer> deque = new LinkedList<>();
//增
//flag为true 单调递减队列
//flag为false 单调递减增队列
public void add(int val,boolean flag){
if(flag){
while(!deque.isEmpty() && val>deque.getLast()){
deque.removeLast();
}
deque.add(val);
}else{
while(!deque.isEmpty() && val<deque.getLast()){
deque.removeLast();
}
deque.add(val);
}
}
//删:删除滑窗的第一个元素,只有相等才需要删除
public void removeFirst(int val){
if(!deque.isEmpty()&&val == deque.peek()){
deque.pollFirst();
}
}
//返回最大值
public int peekMax(){
return deque.peek();
}
}
8. 美团 拆分树(DFS) **
import java.util.*;
public class Main{
static List<Integer>[] edges;
static int[] counts;
static int[] eC;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int N = sc.nextInt();
edges = new List[N+1];
for (int i = 0; i < edges.length; i++) {
edges[i] = new ArrayList<>();
}
for (int i = 1; i < N; i++) {
int to = sc.nextInt();
int from = sc.nextInt();
edges[to].add(from);
edges[from].add(to);
}
eC = new int[N+1];
counts = new int[N+1];
dfs(1,-1);
dfs1(1,-1,N);
int ans = Integer.MAX_VALUE;
int ansCount = 0;
for (int i = 2; i <= N ; i++) {
if (ans == eC[i]){
ansCount++;
}else if (ans>eC[i]){
ans = eC[i];
ansCount = 1;
}
}
System.out.println(ans+" "+ansCount);
}
private static void dfs1(int root, int father,int N) {
List<Integer> edge = edges[root];
for (Integer integer : edge) {
if (integer!=father){
eC[integer] = Math.abs(N-counts[integer]-counts[integer]);
dfs1(integer,root,N);
}
}
}
private static void dfs(int root, int father) {
List<Integer> edge = edges[root];
int ans = 1;
for (Integer integer : edge) {
if (integer!=father){
dfs(integer,root);
ans+=counts[integer];
}
}
counts[root] = ans;
}
}
过了样例 网站没过 可以参考下注释
package ZhenTi;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
/**
* https://codefun2000.com/p/P1177
* 拆分树-dfs 美团
*/
public class Solution35 {
static List<List<Integer>> edges = new ArrayList<>();//记录相连的边
static int[] count;//记录每个节点对应的节点个数
static int[] tempResult;//删除边后(该节点到父节点的边)的两棵树的节点差值
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
//初始化
int n = sc.nextInt();
for(int i=0;i<=n;i++){
edges.add(new ArrayList<>());
}
for(int i=1;i<n;i++){
int child = sc.nextInt();
int parent = sc.nextInt();
edges.get(child).add(parent);
edges.get(parent).add(child);
}
count = new int[n+1];
tempResult = new int[n+1];
//遍历每个节点对应的节点数:更新count
//默认父节点为1
dfs1(1,-1);
//删除每条边后的答案:更新tempResult,不用dfs也可以
dfs2(1,-1,n);
int res = Integer.MAX_VALUE;
int resCount = 0;
for(int i=2;i<=n;i++){
if(res==tempResult[i]){
resCount++;
}else if(res>tempResult[i]){
res = tempResult[i];
resCount=1;
}
}
System.out.println(res+" "+resCount);
}
public static void dfs1(int child,int parent){
List<Integer> tempEdge = edges.get(child);
int res = 1;//节点数为1
for(Integer temp:tempEdge){//遍历每个子节点
if(temp!=parent){
dfs1(temp,child);
res += count[temp];//所有子节点的累加
}
}
count[child] = res;
}
public static void dfs2(int child,int parent,int n){
List<Integer> edge = edges.get(child);
for(Integer temp:edge){
if(temp!=parent){
tempResult[temp] = Math.abs(n-count[temp]-count[temp]);
dfs2(temp,child,n);
}
}
}
}
9. 阿里 联通块 (并查集) **
分两种情况,遇到R就变成W,W不用变化;处理逻辑:拿到点,往下搜索满足条件就插入合并为同一组
import java.util.Scanner;
public class Main{
static int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; // 上下左右四个方向
static int[] father; // 并查集数组
static int numRedBlocks; // 红色连通块个数
public static void main(String[] args) {
//初始化
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = scanner.nextInt();
scanner.nextLine(); // 读取换行符
char[][] matrix = new char[n][m];
for (int i = 0; i < n; i++) {
matrix[i] = scanner.nextLine().replaceAll(" ", "").toCharArray();
}
//
int[][] res = new int[n][m];
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if (matrix[i][j] == 'R') {
matrix[i][j] = 'W';
res[i][j] = countRedBlock(matrix,n,m);
matrix[i][j] = 'R';
}else{
res[i][j] = countRedBlock(matrix,n,m);
}
}
}
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
System.out.print(res[i][j]+" ");
}
System.out.println();
}
}
public static int countRedBlock(char[][] matrix,int n,int m) {
father = new int[n * m];//并查集
numRedBlocks = 0;
//计算红色区域数量
for (int i = 0; i < n * m; i++) {
father[i] = i;
if (matrix[i / m][i % m] == 'R') {
numRedBlocks++;
}
}
//合并红色格子
for (int i = 0; i < n * m; i++) {
int row = i / m;
int col = i % m;
if (matrix[row][col] == 'R') {
for (int[] dir : dirs) {
int nRow = row + dir[0];
int nCol = col + dir[1];
//遇到R:就往下走
if (nRow >= 0 && nCol >= 0 && nRow < n && nCol < m && matrix[nRow][nCol] == 'R') {
union(i,nRow*m+nCol);//若两个父节点不一致就合并且减去红色区域数量
}
}
}
}
return numRedBlocks;
}
//找父节点
public static int findRoot(int x){
if (x != father[x]) {
father[x] = findRoot(father[x]);
}
return father[x];
}
//合并:x插入y
public static void union(int x,int y){
int rootX = findRoot(x);
int rootY = findRoot(y);
if(rootX!=rootY){
father[rootX] = rootY;
numRedBlocks--;
}
}
}
10. 阿里 数组重排 (贪心)
最小对最小,最大对最大;a排序后,b用一个二维数组记录索引,排序后再根据索引求a
package ZhenTi;
import java.util.Arrays;
import java.util.Scanner;
/**
* https://codefun2000.com/p/P1100
*/
public class Solution37 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] a = new int[n];
int[][] b = new int[n][2];
for(int i=0;i<n;i++){
a[i] = sc.nextInt();
}
for(int i=0;i<n;i++){
b[i][0] = sc.nextInt();
b[i][1] = i;
}
Arrays.sort(a);
Arrays.sort(b,(x,y)->{
return x[0]-y[0];
});//数组b按数字大小排序
//把a的值赋值到b
for(int i=0;i<n;i++){
b[i][0] = a[i];
}
//对更新后的b按索引排序
Arrays.sort(b,(x,y)->{
return x[1]-y[1];
});
for(int i=0;i<n;i++){
System.out.print(b[i][0]+" ");
}
}
}
11.美团 必经之路(树上DFS) **
往两边dfs
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main{
static List<List<Integer>> edges = new ArrayList<>();
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
for(int i=0;i<n;i++){
edges.add(new ArrayList<>());
}
for(int i=2;i<=n;i++){
int num = sc.nextInt();
edges.get(num-1).add(i-1);
edges.get(i-1).add(num-1);
}
int x = sc.nextInt()-1;
int y = sc.nextInt()-1;
System.out.println(dfs(x,y)+dfs(y,x)+1);//需要加1 本身
}
public static int dfs(int start,int end){
int res = 0;
for(int num:edges.get(start)){
if(num!=end){
res = Math.max(res,dfs(num,start)+1);
}
}
return res;
}
}
12. 米哈游 RGB矩阵(DFS岛屿数量进阶版)
添加链接描述
正常DFS一次,染色后DFS一次,求连通区域
package ZhenTi;
import java.util.Arrays;
import java.util.Scanner;
/**
* https://codefun2000.com/p/P1094
* 岛屿数量进阶版
*/
public class Solution39 {
static char[][] colors;
static boolean[][] visited;
static int[][] dirs = {{-1,0},{1,0},{0,1},{0,-1}};
static int m;
static int n;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
m = sc.nextInt();
n = sc.nextInt();
sc.nextLine();
colors = new char[m][n];
visited = new boolean[m][n];
for(int i=0;i<m;i++){
colors[i] = sc.nextLine().toCharArray();
}
//整除情况
int count1=0;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(!visited[i][j]){
dfs(i,j);
count1++;
}
}
}
int count2 = 0;
//染色
for(int i=0;i<m;i++){
Arrays.fill(visited[i],false);
}
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(colors[i][j]=='G'){
colors[i][j] = 'B';
}
}
}
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(!visited[i][j]){
dfs(i,j);
count2++;
}
}
}
System.out.println(count1-count2);
}
public static void dfs(int x,int y){
visited[x][y] = true;
for(int[] dir:dirs){
int nx = x + dir[0];
int ny = y + dir[1];
if(nx>=0&&ny>=0&&nx<m&&ny<n&&colors[x][y]==colors[nx][ny]&&!visited[nx][ny]){
dfs(nx,ny);
}
}
}
}
13. 华为od 开心消消乐 (BFS)
package ZhenTi;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
/**
* https://codefun2000.com/p/P1275
* 华为 开心消消乐
*/
public class Solution40 {
static int m;
static int n;
static int[][] nums;
static int[][] dirs = {{-1,0},{1,0},{0,1},{0,-1},{1,-1},{1,1},{-1,1},{-1,-1}};
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
m = sc.nextInt();
n = sc.nextInt();
nums = new int[m][n];
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
nums[i][j] = sc.nextInt();
}
}
int res = 0;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(nums[i][j]==1){
bfs(i,j);//遇到1就开始染色
res++;
}
}
}
System.out.println(res);
}
public static void bfs(int x,int y){
Queue<int[]> que = new LinkedList<>();
que.offer(new int[]{x,y});
while(!que.isEmpty()){
int[] temp = que.poll();
for(int[] dir:dirs){
int nx = temp[0] + dir[0];
int ny = temp[1] + dir[1];
if(nx>=0&&nx<m&&ny>=0&&ny<n&&nums[nx][ny]==1){
nums[nx][ny] = 0;//更新染色点
que.offer(new int[]{nx,ny});
}
}
}
}
}
14. 华为 跑步(BFS) **
BFS Queue(X,Y,速度) + 状态记录
输出不好处理
package ZhenTi;
/**
* https://codefun2000.com/p/P1295
* 华为 BFS 加一个维度
*/
import java.util.*;
public class Solution41 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String[] s = sc.nextLine().trim().split(",");
int n = Integer.parseInt(s[0]), m = Integer.parseInt(s[1]);
s = sc.nextLine().trim().split(",");
int sx = Integer.parseInt(s[0]), sy = Integer.parseInt(s[1]);
int[][] h = new int[n][m], o = new int[n][m];
s = sc.nextLine().trim().replace(" ", ",").split(",");
for (int i = 0, idx = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
h[i][j] = Integer.parseInt(s[idx++]);
}
}
s = sc.nextLine().trim().replace(" ", ",").split(",");
for (int i = 0, idx = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
o[i][j] = Integer.parseInt(s[idx++]);
}
}//输入
// 状态记录
Map<Integer, Set<Integer>> map = new HashMap<>();
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
map.put(i * m + j, new HashSet<>());//对于每一个点开辟一个set记录出现过的速度
}
}
Queue<int[]> q = new LinkedList<>();//开辟队列
map.get(sx * m + sy).add(1);
q.offer(new int[]{sx, sy, 1});//初始点入队列
int cnt = 0;
int[][] dirs = {{-1, 0}, {1, 0}, {0, 1}, {0, -1}};
while (!q.isEmpty()) {
int[] t = q.poll();
int x = t[0], y = t[1], v = t[2];
for (int[] dir : dirs) {
int a = x + dir[0], b = y + dir[1];
if (a < 0 || a >= n || b < 0 || b >= m) continue;//上面几句BFS经典操作
int nv = v + h[x][y] - h[a][b] - o[a][b];//求得速度
if (nv <= 0 || map.get(a * m + b).contains(nv)) continue;//一旦发现(x,y,v)已经被记录或者v<=0时就跳过
if (nv == 1) cnt++;//如果(x,y,1)第一次发生,答案就加1
map.get(a * m + b).add(nv);//更新map,存储每一个(x,y,v)
q.offer(new int[]{a, b, nv});//BFS经典操作
}
}
System.out.println(cnt);
}
}
15. 美团 RGB树(树形dp)
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int size = sc.nextInt();
List<Integer>[] edges = new List[size+1];
for(int i=1;i<=size;i++){
edges[i] = new ArrayList<>();
}
for(int i=2;i<=size;i++){
int n = sc.nextInt();
edges[i].add(n);
edges[n].add(i);
}
char[] colors = new char[size+1];
sc.nextLine();
String str = sc.nextLine();
for(int i=0;i<size;i++){
colors[i+1] = str.charAt(i);
}
//处理
//dp[i][3] 第i个节点为根节点,对应的三种颜色的数量
//dfs1记录每个点
//dfs2找到切割方案的次数
int[][] dp = new int[size+1][3];
boolean[] visited = new boolean[size+1];
dfs1(edges,colors,visited,dp,1);
Arrays.fill(visited,false);
System.out.println(dfs2(edges,colors,visited,dp,1));
}
public static void dfs1(List<Integer>[] edges,char[] colors,boolean[] visited,int[][] dp,int index){
//更新访问状态,遍历三种颜色
visited[index] = true;
if(colors[index]=='R'){
dp[index][0]++;
}
if(colors[index]=='G'){
dp[index][1]++;
}
if(colors[index]=='B'){
dp[index][2]++;
}
//遍历所连接的每一条边
for(int temp:edges[index]){
if(visited[temp]){continue;}
dfs1(edges,colors,visited,dp,temp);//更新子节点
for(int i=0;i<3;i++){
dp[index][i] += dp[temp][i];//父节点等于子节点之和
}
}
}
public static int dfs2(List<Integer>[] edges,char[] colors,boolean[] visited,int[][] dp,int index){
int res = 0;
visited[index] = true;
if(index!=1&&check(dp[index],dp[1])){
res++;
}
for(int next:edges[index]){
if(visited[next]) {continue;}
res += dfs2(edges,colors,visited,dp,next);
}
return res;
}
//判断左子树或者右子树是否满足条件
public static boolean check(int[] cur,int[] root){
for(int i=0;i<3;i++){
if(cur[i]==0||root[i]-cur[i]==0){
return false;
}
}
return true;
}
}
16. 阿里巴巴 满二叉树的数量(记忆化搜索)
import java.util.Arrays;
import java.util.Scanner;
/**
* https://codefun2000.com/p/P1082
* 阿里 树形dp (记忆化搜索)
*/
class TreeNode{
int val;
TreeNode left;
TreeNode right;
public TreeNode(int val){
this.val = val;
}
}
public class Main {
static int[] dp;//-2表示未搜索过 -1表示不是满二叉树 n表示节点个数
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
dp = new int[n+1];
Arrays.fill(dp,-2);//要先初始化为-2
TreeNode[] nodes = new TreeNode[n+1];
for(int i=1;i<n+1;i++){
nodes[i] = new TreeNode(i);
}
for(int i=1;i<=n;i++){
int left = sc.nextInt();
int right = sc.nextInt();
if(left!=-1) nodes[i].left = nodes[left];
if(right!=-1) nodes[i].right = nodes[right];
}
int res = 0;
for(int i=1;i<=n;i++){
if(dfs(nodes[i])>=0){res++;}
}
System.out.println(res);
}
//以节点node开始搜索,三种情况:1.已经搜索过;2.
public static int dfs(TreeNode node){
if(node == null) return 0;
if(dp[node.val]!=-2) return dp[node.val];//记忆化搜索
int left = dfs(node.left);
int right = dfs(node.right);
if(left==-1||right==-1||left!=right){
dp[node.val] = -1;//不是满二叉树
}else{
dp[node.val] = left+1;
}
return dp[node.val];
}
}
17. 美团 南北对决 (前缀和)
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
while(n>0){
int m = sc.nextInt();
int[] nums = new int[m];
int[] res = new int[m];
for(int i=0;i<m;i++){
nums[i] = sc.nextInt();
}
//前到后遍历
int[][] prev = new int[m][2];
if(nums[0]==1){
prev[0][1] = 1;
}else{
prev[0][0] = 0;
}
for(int i=1;i<m;i++){
if(nums[i]==0){
prev[i][0] = prev[i-1][0] + 1;
prev[i][1] = prev[i-1][1];
}else{
prev[i][1] = prev[i-1][1] + 1;
prev[i][0] = prev[i-1][0];
}
}
int[] right = new int[2];
right[0] = prev[m-1][0];
right[1] = prev[m-1][1];
//后到前遍历
int[][] next = new int[m][2];
if(nums[m-1]==1){
next[m-1][1] = 1;
}else{
next[m-1][0] = 0;
}
for(int i=m-2;i>=0;i--){
if(nums[i]==0){
next[i][0] = next[i+1][0] + 1;
next[i][1] = next[i+1][1];
}else{
next[i][1] = next[i+1][1] + 1;
next[i][0] = next[i+1][0];
}
}
int[] left = new int[2];
left[0] = next[0][0];
left[1] = next[0][1];
//
for(int i=0;i<m;i++){
if(nums[i]==0){
res[i] = right[0]-prev[i][0]+left[1]-next[i][1];
}else{
res[i] = right[1]-prev[i][1]+left[0]-next[i][0];
}
System.out.print(res[i]+" ");
}
System.out.println("");
n--;
}
}
}
18. 美团 染色的树 (DFS)
import java.util.*;
public class Main{
static List<List<Integer>> child = new ArrayList<>();
static List<Integer> colors = new ArrayList<>();
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
for(int i=0;i<n;i++){
child.add(new ArrayList<>());
}
for(int i= 1;i<n;i++){
child.get(sc.nextInt()-1).add(i);
}
for(int i=0;i<n;i++){
colors.add(sc.nextInt());
}
System.out.println(dfs(0));
}
public static int dfs(int n){
if(child.get(n).isEmpty()){//空节点返回1
return 1;
}
int sum = 0;
//1个节点和2个节点 A^0=A A+0=0
for(int next:child.get(n)){
if(colors.get(n)==1){
sum += dfs(next);//红色累加
}else{
sum ^= dfs(next);//绿色异或
}
}
return sum;
}
}
19. 百度 排列数量 (技巧)
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
while(n-->0){
int m = sc.nextInt();
int[][] nums = new int[m][2]; //[数字,索引]
for(int j=0;j<m;j++){
nums[j][0] = sc.nextInt();
nums[j][1] = j;
}
//按照数字大小排序
Arrays.sort(nums,(a,b)->(a[0]-b[0]));
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
int res = 0;
//索引范围包含数字即是排列
for(int k=0;k<m;k++){
max = Math.max(max,nums[k][1]);
min = Math.min(min,nums[k][1]);
if(max-min<=k){
res++;
}
}
System.out.println(res);
}
}
}
20. 腾讯音乐 价值二叉树 (树形DFS)
import java.util.*;
public class Main{
static int size;
static List<List<Integer>> joinList;
static int[] value;
static int[] res;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
size = sc.nextInt();
joinList = new ArrayList<>();
value = new int[size];
res = new int[size];
for(int i=0;i<size;i++){
value[i] = sc.nextInt();
}
for(int i=0;i<size;i++){
joinList.add(new ArrayList<>());
}
for(int i=0;i<size-1;i++){
int from = sc.nextInt()-1;
int to = sc.nextInt()-1;
joinList.get(from).add(to);
}
dfs(0);
for(int i=0;i<size;i++){
System.out.print(res[i]+" ");
}
}
public static int[] dfs(int index){
int[] split = splitTwoFive(value[index]); //计算每个数分解成2和5的个数
int[] tempRes = new int[2];
for(int next:joinList.get(index)){
tempRes = dfs(next);//取子节点的值,更新
split[0] += tempRes[0];
split[1] += tempRes[1];
}
res[index] = Math.min(split[0],split[1]);//结果由最小值决定
return split;
}
public static int[] splitTwoFive(int node){
int count2 = 0;
int count5 = 0;
while(node != 0){
if(node%2==0){
count2++;
node = node/2;
}else{
break;
}
}
while(node != 0){
if(node%5==0){
count5++;
node = node/5;
}else{
break;
}
}
return new int[]{count2,count5};
}
}