当前位置:   article > 正文

有向图的最大环数

有向图最大环

leetcode 854

问题描述

给定两个等长字符串A和B,它们所含的字符个数及种类完全一样,问最少需要对A执行多少次交换字符才能使得A变成B?

分析

因为这个问题数据规模很小,只包含6种字符、A和B的长度都不超过20,所以暴力+适当剪枝的思路就能够通过。

首先对于A[i]==B[i]的部分,完全不需要做任何处理;
其次,对于A[i]!=B[i]的部分,显然需要找A[j]来跟A[i]进行交换,A[j]满足A[j]==B[i]。在这个过程中,如果A[i]==B[j],那自然是“意外之喜”,“一箭双雕”,“一石二鸟”。可以很自信的想:如果能够一箭双雕,必然是最优策略。但是,如果没有“一箭双雕”,那就只能逐个尝试寻找最优的 j 了。假设就选择了j,交换完后得到新的字符串A',可以递归调用求solve(A’,B)。
在这个递归过程中,因为B是不变的,这个函数只要A确定,返回值就定下来了。所以可以用备忘录方法(记忆化搜索)来加速递归。

站在更宏观的角度考虑这个问题,把每个A字符串当做结点,每一次swap操作会形成新的结点并添加一条边,以上递归的过程相当于深度优先搜索。如果改写成广度优先搜索,运行效率必定能够提高。

站在更宏观的角度考虑这个问题,这是一个很艰难的图论问题。问题等价于寻找有向图的边的一个覆盖,使得每一个子集都是环,要使环数最大。这个问题似乎是个NP问题。
但是贪心的方式足以通过此题。
贪心法则如下:

  • 选择每个顶点的最小环构成一个最小环集合,对此集合执行去重操作。
  • 如果环集合中存在结点数为1的环,必然选择之。
  • 如果环集合中存在结点数为2的环,必然选择之。
  • 否则,执行以下步骤。
  • 对于这个环集合,统计图中边的使用次数。
  • 对每个环,求它边的平均使用次数作为这个环的value。
  • 优先消去value最小的环

这个问题等价于:
给定一个可以包含重复元素的数组,最少需要执行多少次swap操作,才能使数组变得有序。

C++递归写法

  1. class Solution {
  2. public:
  3. unordered_map<string,int> mp;
  4. int kSimilarity(string A, string B) {
  5. if(A<B) return kSimilarity(B,A);
  6. if(mp[A+B]) return mp[A+B];
  7. int i=0;
  8. while(i<A.size() && A[i]==B[i]) i++;
  9. if(i==A.size()) return 0;
  10. int j=i+1;
  11. vector<int> pos;
  12. while(j<A.size()){
  13. if(A[j]==B[i]){
  14. if(A[i]==B[j]){
  15. pos.clear();
  16. pos.push_back(j);
  17. break;
  18. }
  19. pos.push_back(j);
  20. };
  21. j++;
  22. }
  23. int res=INT_MAX;
  24. for(int p:pos){
  25. swap(A[i],A[p]);
  26. res=min(res,kSimilarity(A.substr(i+1),B.substr(i+1)));
  27. swap(A[i],A[p]);
  28. }
  29. return mp[A+B]=res+1;
  30. }
  31. };

Java非递归写法

  1. class Solution {
  2. public int kSimilarity(String A, String B) {
  3. if (A.equals(B)) return 0;
  4. Set<String> vis= new HashSet<>();
  5. Queue<String> q= new LinkedList<>();
  6. q.add(A);
  7. vis.add(A);
  8. int res=0;
  9. while(!q.isEmpty()){
  10. res++;
  11. for (int sz=q.size(); sz>0; sz--){
  12. String s= q.poll();
  13. int i=0;
  14. while (s.charAt(i)==B.charAt(i)) i++;
  15. for (int j=i+1; j<s.length(); j++){
  16. if (s.charAt(j)==B.charAt(j) || s.charAt(i)!=B.charAt(j) ) continue;
  17. String temp= swap(s, i, j);
  18. if (temp.equals(B)) return res;
  19. if (vis.add(temp)) q.add(temp);
  20. }
  21. }
  22. }
  23. return res;
  24. }
  25. public String swap(String s, int i, int j){
  26. char[] ca=s.toCharArray();
  27. char temp=ca[i];
  28. ca[i]=ca[j];
  29. ca[j]=temp;
  30. return new String(ca);
  31. }
  32. }

Java贪心法

  1. import java.io.FileInputStream;
  2. import java.io.FileNotFoundException;
  3. import java.util.*;
  4. import java.util.stream.Collectors;
  5. class Solution {
  6. /**
  7. * 构图,构完图之后,两个字符串就可以丢掉了
  8. */
  9. int[][] buildGraph(char[] a, char[] b) {
  10. TreeMap<Character, Integer> ma = new TreeMap<>();
  11. for (char i : a) {
  12. if (!ma.containsKey(i)) {
  13. ma.put(i, ma.size());
  14. }
  15. }
  16. for (char j : b) {
  17. if (!ma.containsKey(j)) {
  18. ma.put(j, ma.size());
  19. }
  20. }
  21. int g[][] = new int[ma.size()][ma.size()];
  22. for (int i = 0; i < a.length; i++) {
  23. int from = ma.get(b[i]), to = ma.get(a[i]);
  24. g[from][to] += 1;
  25. }
  26. return g;
  27. }
  28. /**
  29. * 计算结点node的出度
  30. */
  31. int outEdge(int node, int[][] g) {
  32. return Arrays.stream(g[node]).sum();
  33. }
  34. int[][] copyGraph(int[][] g) {
  35. int[][] a = new int[g.length][g.length];
  36. for (int i = 0; i < g.length; i++) {
  37. for (int j = 0; j < g.length; j++) {
  38. a[i][j] = g[i][j];
  39. }
  40. }
  41. return a;
  42. }
  43. /**
  44. * 寻找node结点所在的最小环
  45. */
  46. List<List<Integer>> findMinRingOf(int node, int[][] g) {
  47. g = copyGraph(g);
  48. List<List<Integer>> rings = new LinkedList<>();
  49. while (outEdge(node, g) > 0) {
  50. int[] prev = new int[g.length];//记录最小环的路径
  51. Arrays.fill(prev, -1);
  52. Queue<Integer> q = new LinkedList<>();
  53. q.add(node);
  54. out:
  55. while (!q.isEmpty()) {
  56. Integer i = q.poll();
  57. for (int j = 0; j < g[i].length; j++) {
  58. if (g[i][j] > 0) {
  59. if (prev[j] != -1) continue;//已经访问过了就不再访问了
  60. prev[j] = i;
  61. q.add(j);//准备扩展j结点
  62. if (j == node) {//找到了
  63. break out;
  64. }
  65. }
  66. }
  67. }
  68. ArrayList<Integer> a = new ArrayList<>(g.length);
  69. a.add(node);
  70. int now = node;
  71. while (true) {
  72. int next = prev[now];
  73. if (next == node) break;
  74. a.add(next);
  75. now = next;
  76. }
  77. //翻转数组
  78. for (int i = 0; i < a.size() >> 1; i++) {
  79. int temp = a.get(i);
  80. a.set(i, a.get(a.size() - 1 - i));
  81. a.set(a.size() - 1 - i, temp);
  82. }
  83. if (rings.isEmpty() || rings.get(0).size() == a.size()) {
  84. rings.add(a);
  85. } else {
  86. break;
  87. }
  88. removeRing(a, g);
  89. }
  90. return rings;
  91. }
  92. /**
  93. * 用完一个环之后,把环删除
  94. */
  95. void removeRing(List<Integer> ring, int[][] g) {
  96. for (int i = 0; i < ring.size(); i++) {
  97. g[ring.get(i)][(ring.get((i + 1) % ring.size()))]--;
  98. }
  99. }
  100. /**
  101. * 贪心寻找图中最优环
  102. */
  103. List<Integer> findMinRing(int[][] g) {
  104. List<List<Integer>> rings = new LinkedList<>();//全部环构成的集合
  105. for (int i = 0; i < g.length; i++) {
  106. if (outEdge(i, g) > 0) {
  107. List<List<Integer>> r = findMinRingOf(i, g);
  108. rings.addAll(r);
  109. }
  110. }
  111. //去重
  112. Set<String> had = new TreeSet<>();
  113. LinkedList<List<Integer>> uniqRings = new LinkedList<>();
  114. for (List<Integer> ring : rings) {
  115. String k = ring.stream().sorted().map(x -> x + "").collect(Collectors.joining(","));
  116. if (!had.contains(k)) {
  117. had.add(k);
  118. uniqRings.add(ring);
  119. }
  120. }
  121. rings = uniqRings;
  122. //统计每条边的使用次数
  123. double[][] use = new double[g.length][g.length];
  124. for (List<Integer> ring : rings) {
  125. for (int j = 0; j < ring.size(); j++) {
  126. use[ring.get(j)][ring.get((j + 1) % ring.size())]++;
  127. }
  128. }
  129. rings.sort(Comparator.comparing(x -> {
  130. if (x.size() == 1) return -1.0;//优先级最高
  131. if (x.size() == 2) return 0.0;//优先级次高
  132. double s = 0;
  133. for (int i = 0; i < x.size(); i++) {
  134. s += use[x.get(i)][x.get((i + 1) % x.size())];
  135. }
  136. s /= x.size();
  137. return s;
  138. }));
  139. if (rings.size() == 0) return null;
  140. return rings.get(0);
  141. }
  142. public int kSimilarity(String A, String B) {
  143. char[] a = A.toCharArray(), b = B.toCharArray();
  144. int[][] g = buildGraph(a, b);
  145. int N = a.length;
  146. while (true) {
  147. List<Integer> ring = findMinRing(g);
  148. if (ring == null) break;
  149. N--;
  150. removeRing(ring, g);
  151. }
  152. return N;
  153. }
  154. public static void main(String[] args) {
  155. try {
  156. Scanner cin = new Scanner(new FileInputStream("in.txt"));
  157. System.out.println(new Solution().kSimilarity(cin.next(), cin.next()));
  158. } catch (FileNotFoundException e) {
  159. e.printStackTrace();
  160. }
  161. }
  162. }

转载于:https://www.cnblogs.com/weiyinfu/p/9769966.html

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/64587
推荐阅读
相关标签
  

闽ICP备14008679号