前言 这是华为的笔试刷题笔记1
题目包括:
1 2 3 4 5 6 7 8 9 10 2026年1月22日-100分-投资最大收益周期 2025年7月9日-300分-无线网络覆盖计划 2024年11月20日-200分-服务器休息计划 2022年10月12日-100分-D路通信 2025年10月17日-100分-奖品兑换组合 2025年5月14日-200分-游戏中的地图穿越 2025年6月11日-200分-网络整改 2025年6月11日-300分-命令行参数提示 2023年10月12日-200分-滑雪冒险 2023年05月10日-300分-快乐校园跑
题目详解 1)投资最大收益周期
常规动态规划,转移方程为dp[i] = max(dp[i - 1] + nums[i], nums[i]);
本题是取一段连续的最大子数组,因此考虑是否加入nums[i]
2)无线网络覆盖计划
本题是典型的01背包问题:倒序遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class Main { public static int zeroOneKnapsack (int [] weight, int [] value, int bagSize) { int n = weight.length; int [] dp = new int [bagSize + 1 ]; for (int i = 0 ; i < n; i++) { for (int j = bagSize; j >= weight[i]; j--) { dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]); } } return dp[bagSize]; } public static void main (String[] args) { int [] weight = {1 , 3 , 4 }; int [] value = {15 , 20 , 30 }; int bagSize = 4 ; System.out.println(zeroOneKnapsack(weight, value, bagSize)); } }
3)服务区休息计划
dp[i]表示在第i个服务区休息的情况下的最小花费
注意初始化dp时,前M个服务区初始化的dp值是它们本身的花费
最后取倒数M个服务区的最小花费即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import java.util.*;public class Main { public static void main (String[] args) { Scanner in = new Scanner (System.in); int N = in.nextInt(); int M = in.nextInt(); int [] cost = new int [N]; for (int i = 0 ; i < N; ++i){ cost[i] = in.nextInt(); } int [] dp = new int [N]; Arrays.fill(dp, Integer.MAX_VALUE); for (int i = 0 ; i < Math.min(N, M); i++) { dp[i] = cost[i]; } for (int i = 1 ; i < N; ++i){ for (int j = i - 1 ; j >= i - M && j >= 0 ; --j){ dp[i] = Math.min(dp[i], dp[j] + cost[i]); } } int min = Integer.MAX_VALUE; for (int i = N - 1 ; i>= N - M && i >= 0 ;--i){ min = Math.min(min, dp[i]); } System.out.println(min); } }
4)D路通信
注意最长公共子串和最长公共子序列的区别:
子串一定是连续的,因此如果当前i和j的字母不同,dp(i)(j)为0
对于最长公共子序列,需要将状态转移给dp(i-1)(j)或dp(i)(j-1)
5)奖券兑换组合
01背包:最多选1次
完全背包:可以选无限次
多重背包:最多选指定次数
本题是多重背包:
1 2 3 4 5 dp[i][s] = dp[i - 1 ][s] + dp[i - 1 ][s - v] + dp[i - 1 ][s - 2v] + ... + dp[i - 1 ][s - cnt * v]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import java.util.*;public class Main { public static void main (String[] agrs) { Scanner in = new Scanner (System.in); int len = in.nextInt(); Map<Integer, Integer> map = new HashMap <>(); for (int i = 0 ; i < len; ++i){ int value = in.nextInt(); map.put(value, map.getOrDefault(value, 0 ) + 1 ); } int num = in.nextInt(); int [] dp = new int [num + 1 ]; dp[0 ] = 1 ; for (Map.Entry<Integer, Integer> e : map.entrySet()){ int v = e.getKey(); int cnt = e.getValue(); int [] next = new int [num + 1 ]; for (int i = 0 ; i < num + 1 ; ++i){ for (int k = 0 ; k <= cnt && k * v <= i; ++k){ next[i] += dp[i - k * v]; } } dp = next; } System.out.println(dp[num]); } }
方案数最开始初始化为1
熟悉Entry的写法
熟悉多重背包的写法
6)游戏中的地图穿越
二维dp,但不能使用整型最大值,可能会出现溢出的情况
7)网络整改(学习80min)
错误代码,,错误原因在于移除节点后,剩余的节点有可能会升级成叶子节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 import java.util.*; public class Main{ static int dis = 0; public static void main(String[] args){ Scanner in = new Scanner(System.in); int n = in.nextInt(); ArrayList<List<Integer>> list = new ArrayList<>(); for(int i = 0; i < n; ++i){ list.add(new ArrayList<>()); } for(int i = 0; i < n - 1; ++i){ int first = in.nextInt(); int second = in.nextInt(); list.get(first - 1).add(second); } // ArrayList<Integer> edge = new ArrayList<>(); // for(int i = 0; i < n; ++i){ // if(list.get(i).size() == 0) // edge.add(i + 1); // } ArrayList<Integer> distance = new ArrayList<>(); dfs(distance, list, 0); //找到distance数组中出现次数最多的频率 Map<Integer, Integer> map = new HashMap<>(); for(int i = 0; i < distance.size(); ++i){ map.put(distance.get(i), map.getOrDefault(distance.get(i), 0) + 1); } ArrayList<Integer> ans = new ArrayList<>(); for(int cnt : map.values()){ ans.add(cnt); } ans.sort(Collections.reverseOrder()); System.out.println(distance.size() - ans.get(0)); } static void dfs(ArrayList<Integer> distance,ArrayList<List<Integer>> list , int root){ if(list.get(root).size() == 0){ distance.add(dis); } for(int i = 0; i < list.get(root).size(); ++i){ dis++; dfs(distance, list, list.get(root).get(i) - 1); dis--; } } }
正确代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 import java.util.*;public class Main { static List<List<Integer>> adj; static List<List<Integer>> children; static int maxDepth = 0 ; static int [] depth; static void dfs (int u, int p) { for (int v : adj.get(u)){ if (v == p) continue ; depth[v] = depth[u] + 1 ; maxDepth = Math.max(depth[v], maxDepth); children.get(u).add(v); dfs(v, u); } } public static void main (String[] args) { Scanner in = new Scanner (System.in); int n = in.nextInt(); adj = new ArrayList <>(); for (int i = 0 ; i <= n; ++i) adj.add(new ArrayList <>()); for (int i = 0 ; i < n - 1 ; ++i){ int first = in.nextInt(); int second = in.nextInt(); adj.get(first).add(second); } depth = new int [n + 1 ]; children = new ArrayList <>(); for (int i = 0 ; i <= n; ++i) children.add(new ArrayList <>()); dfs(1 , 0 ); int ans = 0 ; List<List<Integer>> byDepth = new ArrayList <>(); for (int i = 0 ; i <= maxDepth; ++i) byDepth.add(new ArrayList <>()); for (int v = 1 ; v <= n; ++v) byDepth.get(depth[v]).add(v); for (int h = 0 ; h <= maxDepth; ++h){ int [] best = new int [n + 1 ]; Arrays.fill(best, Integer.MIN_VALUE/2 ); for (int d = maxDepth; d >= 0 ; d--){ for (int v: byDepth.get(d)){ if (d > h){ best[v] = Integer.MIN_VALUE/2 ; }else if (d == h){ best[v] = 1 ; } else { int sum = 0 ; for (int u: children.get(v)){ if (best[u] > 0 ) sum += best[u]; } best[v] = (sum > 0 ? sum + 1 : Integer.MIN_VALUE/2 ); } } } ans = Math.max(ans, best[1 ]); } System.out.println(n - ans); } }
核心思路
1、树型dp
2、删除最少 转换成保留最多
8)命令行参数提示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 import java.util.*;public class Main { static class Node { int index; int distance; public Node (int i, int d) { index = i; distance = d; } } public static void main (String[] args) { Scanner in = new Scanner (System.in); int D = in.nextInt(); int N = in.nextInt(); String[] demands = new String [N]; for (int i = 0 ; i < N; ++i){ demands[i] = in.next(); } String target = in.next(); int [] distances = new int [N]; boolean cnt = false ; for (int i = 0 ; i < N; ++i){ distances[i] = editDistance(demands[i], target); if (distances[i] == 0 ) { System.out.println(demands[i]); return ; }else if (distances[i] <= D){ cnt = true ; } } if (!cnt){ System.out.println("None" ); }else { Node[] nodes = new Node [N]; for (int i = 0 ; i < N; ++i){ nodes[i] = new Node (i, distances[i]); } Arrays.sort(nodes, (a, b) -> { if (a.distance != b.distance) { return Integer.compare(a.distance, b.distance); } return demands[a.index].compareTo(demands[b.index]); }); for (int i = 0 ; i < N; ++i){ if (nodes[i].distance <= D){ System.out.print(demands[nodes[i].index] + " " ); }else { break ; } } } } static int editDistance (String mm, String nn) { int m = mm.length(); int n = nn.length(); int [][] dp = new int [m + 1 ][n + 1 ]; for (int i = 0 ; i < m + 1 ; ++i){ dp[i][0 ] = i; } for (int j = 0 ; j < n + 1 ; ++j){ dp[0 ][j] = j; } for (int i = 1 ; i < m + 1 ; ++i){ for (int j = 1 ; j < n + 1 ; ++j){ int left = dp[i][j - 1 ] + 1 ; int down = dp[i - 1 ][j] + 1 ; int leftDown = dp[i - 1 ][j - 1 ]; if (mm.charAt(i - 1 ) != nn.charAt(j - 1 )) leftDown += 1 ; dp[i][j] = Math.min(left, Math.min(down, leftDown) ); } } return dp[m][n]; } }
注意在写边界+1的dp时的遍历终点
字典序用的是compareTo
9)滑雪冒险
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 import java.util.*;public class Main { static class Node { int h; int i; int j; public Node (int h, int i, int j) { this .h = h; this .i = i; this .j = j; } } public static void main (String[] args) { Scanner in = new Scanner (System.in); int R = in.nextInt(); int C = in.nextInt(); int [][] map = new int [R][C]; Node[] nodes = new Node [R * C]; int k = 0 ; for (int i = 0 ; i < R; ++i){ for (int j = 0 ; j < C; ++j){ map[i][j] = in.nextInt(); nodes[k] = new Node (map[i][j], i, j); k++; } } Arrays.sort(nodes, (a , b) -> b.h - a.h); int [][] dp = new int [R][C]; int [][] d = new int [][]{{-1 , 0 },{1 ,0 },{0 , -1 }, {0 , 1 }}; int max = 0 ; for (int i = 1 ; i < R*C; ++i){ int h = nodes[i].h; int m = nodes[i].i; int n = nodes[i].j; for (int j = 0 ; j < 4 ; ++j){ int ii = m + d[j][0 ], jj = n + d[j][1 ]; if (ii >= 0 && jj >=0 && ii < R && jj < C && map[ii][jj] > h){ dp[m][n] = Math.max(dp[m][n], dp[ii][jj] + 1 ); } } max = Math.max(max, dp[m][n]); } System.out.println(max + 1 ); } }
把最高点作为初始状态,然后依次遍历,是排序+动态规划
第二种方法是dfs+记忆化搜索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 import java.util.*;public class Main { static int [][] g = new int [210 ][210 ]; static int [][] f = new int [210 ][210 ]; static int n, m; static int [] dx = {-1 , 1 , 0 , 0 }, dy = {0 , 0 , 1 , -1 }; static int dfs (int x, int y) { if (f[x][y] != -1 ) return f[x][y]; f[x][y] = 1 ; int t = 0 ; for (int i = 0 ; i < 4 ; i++) { int a = dx[i] + x, b = dy[i] + y; if (a < 0 || a >= n || b < 0 || b >= m || g[a][b] >= g[x][y]) continue ; t = Math.max(t, dfs(a, b)); } f[x][y] += t; return f[x][y]; } public static void main (String[] args) { Scanner sc = new Scanner (System.in); n = sc.nextInt(); m = sc.nextInt(); for (int i = 0 ; i < n; i++) { for (int j = 0 ; j < m; j++) { g[i][j] = sc.nextInt(); } } for (int i = 0 ; i < n; i++) { Arrays.fill(f[i], -1 ); } int res = 1 ; for (int i = 0 ; i < n; i++) { for (int j = 0 ; j < m; j++) { res = Math.max(res, dfs(i, j)); } } System.out.println(res); } }
10)快乐校园跑(解题75min, 学习45min, 总时长120min)
错误代码,且用时75min,试图用回溯思路解题,但过程中漏洞百出,需要加强回溯的写法,同时也要加强图论部分的学习
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 import java.util.*;public class Main { static ArrayList<ArrayList<Integer>> paths = new ArrayList <>(); public static void main (String[] args) { Scanner in = new Scanner (System.in); int n = in.nextInt(); int m = in.nextInt(); int [][] runTimes = new int [n][n]; for (int i = 0 ; i < m; ++i){ int start = in.nextInt() - 1 ; int end = in.nextInt() - 1 ; int time = in.nextInt(); runTimes[start][end] = time; } int from = in.nextInt(); boolean [] visited = new boolean [n]; for (int i = 0 ; i < runTimes[from-1 ].length; ++i){ if (from != i + 1 && runTimes[from-1 ][i] != 0 ){ ArrayList<Integer> path = new ArrayList <>(); path.add(from); dfs(runTimes, i + 1 ,visited, path); } Arrays.fill(visited, false ); } Set<Integer> set = new HashSet <>(); int [] totolTime = new int [paths.size()]; for (int i = 0 ; i < paths.size(); ++i){ List<Integer> path = paths.get(i); for (int j = 0 ; j < path.size(); ++j){ int k = path.get(j); set.add(k); if (j != 0 ) totolTime[i] += runTimes[path.get(j - 1 )][k] + runTimes[k][k]; } } List<Integer> location_min_time = new ArrayList <>(); for (int location : set){ int min = Integer.MAX_VALUE; for (int i = 0 ; i < paths.size(); ++i){ List<Integer> path = paths.get(i); if (path.contains(location)){ min = Math.min(min, totolTime[i]); } } location_min_time.add(min); } int max = Collections.max(location_min_time); System.out.println(set.size()); System.out.println(max); } static void dfs (int [][] runTimes, int from, boolean [] visited, ArrayList<Integer> path) { if (visited[from - 1 ]){ paths.add(new ArrayList <>(path)); return ; } for (int i = 0 ; i < runTimes[from-1 ].length; ++i){ if (from != i + 1 && runTimes[from-1 ][i] != 0 ){ path.add(from); visited[from - 1 ] = true ; dfs(runTimes, i + 1 , visited, path); } } } }
正确思路:floyd最短路径
算法模板:
1 2 3 4 5 6 7 8 9 10 11 12 13 for (int k = 1 ; k <= n; ++k){ for (int i = 1 ; i <= n; ++i){ if (f[i][k] == INF) continue ; for (int j = 1 ; j <= n; ++j){ if (f[k][j] == INF) continue ; if (f[i][k] + f[k][j] < f[i][j]) f[i][j] = f[i][k] + f[k][j]; } } }
本题核心
1、建模成floyd算法问题(尤其是图中点的数量在100左右)
最多必经点数建模成floyd中的起点的可达点数
最终成绩时间建模成floyd中的起点到各个可达点的最小值
2、考虑小游戏时间添加到a->b的路径长度当中