赞
踩
使用dfs对每一组查询都去找最近公共祖先,并在这个过程中统计边的权重,最后通过TreeMap计算出边权重集合中元素重复的最大次数,贪心策略可知,结果为:查询路径上总共的边-最大次数。
时间复杂度:O( n 2 n^2 n2)
空间复杂度:O( m × n m\times n m×n)
List<Integer> list; public int[] minOperationsQueries(int n, int[][] edges, int[][] queries) { Map<Integer,Integer>[] graph=createGraph(n,edges); int qn=queries.length; int[] res=new int[qn]; for(int i=0;i<qn;i++){ int from=queries[i][0]; int to=queries[i][1]; if(from==to) continue; list=new ArrayList<>(); boolean[] visited=new boolean[n]; dfs(graph,from,to,visited,new ArrayList<>()); res[i]=needChange(list); } return res; } public int needChange(List<Integer> l){ TreeMap<Integer, Long> frequencyMap = new TreeMap<>(l.stream() .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))); TreeMap<Integer, Long> frequencySortMap=new TreeMap<>(Comparator.comparing(frequencyMap::get)); frequencySortMap.putAll(frequencyMap); return l.size()-Integer.parseInt(frequencySortMap.get(frequencySortMap.lastKey()).toString()); } public Map<Integer,Integer>[] createGraph(int n,int[][] edges){ Map<Integer,Integer>[] graph=new HashMap[n]; for(int i=0;i<n;i++) graph[i]=new HashMap<>(); for(int[] e:edges){ int from =e[0]; int to=e[1]; int val=e[2]; graph[from].put(to,val); graph[to].put(from,val); } return graph; } public void dfs(Map<Integer,Integer>[] graph,int from,int to,boolean[] visited,List<Integer> path){ if(from==to){ list=new ArrayList(path); return ; } visited[from]=true; for(int next:graph[from].keySet()){ if(!visited[next]){ path.add(graph[from].get(next)); dfs(graph,next,to,visited,path); path.remove(path.size()-1); } } visited[from]=false; }
参考:官方题解
以节点 0 为根节点,使用数组 count[i]记录节点 i到根节点 0 的路径上边权重的数量,即 count[i][j] 表示节点 i到根节点 0 的路径上权重为 j的边数量。对于查询 queries[i]=[ a i a_i ai, b i b_i bi],记节点 l c a i lca_i lcai为节点 a i a_i ai与 b i b_i bi的最近公共祖先,那么从节点 a i a_i ai到节点 b i b_i bi的路径上,权重为 j 的边数量 t j t_j tj的计算如下:
t j = count [ a i ] [ j ] + count [ b i ] [ j ] − 2 × count [ lca i ] [ j ] t_j = \textit{count}[a_i][j] + \textit{count}[b_i][j] - 2 \times \textit{count}[\textit{lca}_i][j] tj=count[ai][j]+count[bi][j]−2×count[lcai][j]
为了让节点 a i a_i ai到节点 b i b_i bi路径上每条边的权重都相等,贪心地将路径上所有的边都更改为边数量最多的权重即可,即从节点 a i a_i ai到节点 b i b_i bi路径上每条边的权重都相等所需的最小操作次数 r e s i res_i resi的计算如下: res i = ∑ j = 1 W t j − max 1 ≤ j ≤ W t j \textit{res}_i = \sum_{j=1}^{W}t_j - \max_{1 \le j \le W}t_j resi=∑j=1Wtj−max1≤j≤Wtj
其中 W=26W = 26W=26 表示权重的最大值。
时间复杂度:O((m+n)×W+m×logn),其中 n 是节点数目,m 是查询数目,W 是权重的可能取值数目。
空间复杂度:O(n×W+m)
class Solution { static final int W = 26; public int[] minOperationsQueries(int n, int[][] edges, int[][] queries) { int m = queries.length; Map<Integer, Integer>[] neighbors = new Map[n]; for (int i = 0; i < n; i++) { neighbors[i] = new HashMap<Integer, Integer>(); } for (int[] edge : edges) { neighbors[edge[0]].put(edge[1], edge[2]); neighbors[edge[1]].put(edge[0], edge[2]); } List<int[]>[] queryArr = new List[n]; for (int i = 0; i < n; i++) { queryArr[i] = new ArrayList<int[]>(); } for (int i = 0; i < m; i++) { queryArr[queries[i][0]].add(new int[]{queries[i][1], i}); queryArr[queries[i][1]].add(new int[]{queries[i][0], i}); } int[][] count = new int[n][W + 1]; boolean[] visited = new boolean[n]; int[] uf = new int[n]; int[] lca = new int[m]; tarjan(0, -1, neighbors, queryArr, count, visited, uf, lca); int[] res = new int[m]; for (int i = 0; i < m; i++) { int totalCount = 0, maxCount = 0; for (int j = 1; j <= W; j++) { int t = count[queries[i][0]][j] + count[queries[i][1]][j] - 2 * count[lca[i]][j]; maxCount = Math.max(maxCount, t); totalCount += t; } res[i] = totalCount - maxCount; } return res; } public void tarjan(int node, int parent, Map<Integer, Integer>[] neighbors, List<int[]>[] queryArr, int[][] count, boolean[] visited, int[] uf, int[] lca) { if (parent != -1) { System.arraycopy(count[parent], 0, count[node], 0, W + 1); count[node][neighbors[node].get(parent)]++; } uf[node] = node; for (int child : neighbors[node].keySet()) { if (child == parent) { continue; } tarjan(child, node, neighbors, queryArr, count, visited, uf, lca); uf[child] = node; } for (int[] pair : queryArr[node]) { int node1 = pair[0], index = pair[1]; if (node != node1 && !visited[node1]) { continue; } lca[index] = find(uf, node1); } visited[node] = true; } public int find(int[] uf, int i) { if (uf[i] == i) { return i; } uf[i] = find(uf, uf[i]); return uf[i]; } }
困难题果然不是我会做的,做做搬运工得了
有任何问题,欢迎评论区交流,欢迎评论区提供其它解题思路(代码),也可以点个赞支持一下作者哈
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。