1.E题:七段码

image.png
此题巨坑,反正题主走了弯路

1.思路分析:

数码管分为7段,要求的是数码管上不同的灯的亮灭组成是否是一个连通的线,我们需要把题目抽象为两个方面:

  1. 利用dfs来求各种灯亮灭的所有组合。(子集问题)
  2. 在一个子集中,利用并查集将所有的元素联通,最后遍历,如果有一个点与其他节点不连通,则该子集不可能是满足题目条件的结果之一

2.代码

2.1子集问题代码

  1. //标准的回溯算法框架
  2. static List<LinkedList<Integer>> res = new LinkedList<>();
  3. static LinkedList<Integer> list = new LinkedList<Integer>();
  4. public static void backTrack(int[] num, int start) {
  5. //子集问题的回溯条件很特别:没有特定的回溯条件,选择了一个元素以后就可以添加进结果集
  6. res.add(new LinkedList(list));
  7. for (int i = start; i < num.length; i++) {
  8. list.add(num[i]);
  9. backTrack(num, i + 1); //添加下一个元素
  10. list.remove(list.size() - 1);
  11. }
  12. }

2.2并查集代码

  1. public class UF{
  2. int count;
  3. int[] parent;
  4. int[] size;
  5. public UF(int n){
  6. this.count=n;
  7. parent=new int[n];
  8. size=new int[n];
  9. for(int i=0;i<n;i++){
  10. parent[i]=i;
  11. size[i]=1;
  12. }
  13. }
  14. public boolean isconnected(int p,int q){
  15. int rootp=find(p);
  16. int rootq=find(q);
  17. return rootp==rootq;
  18. }
  19. public int find(int node){
  20. while(parent[node]!=node){
  21. parent[node]=parent[parent[node]];
  22. node=parent[node];
  23. }
  24. return node;
  25. }
  26. public void union(int p,int q){
  27. int rootp=find(p);
  28. int rootq=find(q);
  29. if(isconnected(rootp,rootq)){
  30. return;
  31. }
  32. if(size[rootp]>size[rootq]){
  33. parent[rootq]=rootp;
  34. size[rootp]+=size[rootq];
  35. }else{
  36. parent[rootp]=rootq;
  37. size[rootq]+=size[rootp];
  38. }
  39. count--;
  40. }
  41. public int count(){
  42. return count;
  43. }
  44. }

2.3完整代码

  1. package com.lanqiao01;
  2. import java.util.*;
  3. public class Main {
  4. static List<LinkedList<Integer>> res = new LinkedList<>();
  5. static LinkedList<Integer> list = new LinkedList<Integer>();
  6. public static void main(String[] args) {
  7. int[] num = new int[]{1, 2, 3, 4, 5, 6, 7};
  8. int ans = 0;
  9. boolean flag = true;
  10. int[][] e = new int[8][8];
  11. e[1][2] = e[1][6] = 1;
  12. e[2][1] = e[2][7] = e[2][3] = 1;
  13. e[3][2] = e[3][7] = e[3][4] = 1;
  14. e[4][3] = e[4][5] = 1;
  15. e[5][7] = e[5][6] = e[5][4] = 1;
  16. e[6][5] = e[6][7] = e[6][1] = 1;
  17. e[7][2] = e[7][3] = e[7][5] = e[7][6] = 1;
  18. // UF uf = new UF(8);
  19. // for (int row = 1; row < 8; row++) {
  20. // for (int col = 1; col < 8; col++) {
  21. // if (e[row][col] == 1) {
  22. //// if (uf.isconnected(row, col)) {
  23. //// continue;
  24. //// }
  25. // uf.union(row, col);
  26. // }
  27. // }
  28. // } // 完成并查集关系的建立
  29. //上面注释的代码不能加进去,因为一旦对邻接矩阵进行操作,所有的点都将被联通(对于任何一
  30. 条边,可能只有两条或者三条边是联通的,但是对所有的边进行一次联通操作,那么所有的边
  31. 都只会连接到一个父节点上)
  32. backTrack(num, 0);
  33. for (int i = 0; i < res.size(); i++) {
  34. UF uf = new UF(8);
  35. //并查集的建立要在每次判断一个子结果是否符合题意之前,
  36. 否则,如果不进行初始化重新创建,随着联通点的步步增加,所有的点又将重新被联通
  37. List<Integer> cur = res.get(i);
  38. if (cur.size() == 0) {
  39. continue;
  40. } else if (cur.size() == 1) {
  41. ans++;
  42. } else {
  43. for (int j = 0; j < cur.size(); j++) {
  44. for (int k = j + 1; k < cur.size(); k++) {
  45. int a = cur.get(j);
  46. int b = cur.get(k);
  47. if(e[a][b]==1){
  48. uf.union(a,b);
  49. }
  50. }
  51. }
  52. int tmp=cur.get(0);
  53. for (int m=1;m<cur.size();m++){
  54. if(!uf.isconnected(tmp,cur.get(m))){
  55. flag=false;
  56. break;
  57. }
  58. tmp=cur.get(m);
  59. }
  60. if (flag) {
  61. ans++;
  62. }
  63. flag = true;
  64. }
  65. }
  66. // System.out.println(uf.isconnected(e[3][2], e[3][7]));
  67. System.out.println(ans);
  68. System.out.println(res);
  69. System.out.println(res.size());
  70. }
  71. public static void backTrack(int[] num, int start) {
  72. res.add(new LinkedList(list));
  73. for (int i = start; i < num.length; i++) {
  74. list.add(num[i]);
  75. backTrack(num, i + 1);
  76. list.remove(list.size() - 1);
  77. }
  78. }
  79. }
  80. class UF {
  81. int count;
  82. int[] parent;
  83. int[] size;
  84. public UF(int n) {
  85. this.count = n;
  86. // 初始化parent和size数组
  87. parent = new int[n];
  88. size = new int[n];
  89. for (int i = 0; i < n; i++) {
  90. parent[i] = i;
  91. size[i] = 1;
  92. }
  93. }
  94. public boolean isconnected(int p, int q) {
  95. int rootq = find(q);
  96. int rootp = find(p);
  97. return rootp == rootq;
  98. }
  99. public void union(int p, int q) {
  100. int rootp = find(p);
  101. int rootq = find(q);
  102. if (rootp == rootq) {
  103. return;
  104. }
  105. if (size[rootp] > size[rootq]) {
  106. parent[rootq] = rootp;
  107. size[rootp] += size[rootq];
  108. } else {
  109. parent[rootp] = rootq;
  110. size[rootq] += size[rootp];
  111. }
  112. count--;
  113. }
  114. public int find(int x) {
  115. while (parent[x] != x) {
  116. parent[x] = parent[parent[x]];
  117. x = parent[x];
  118. }
  119. return x;
  120. }
  121. public int count() {
  122. return count;
  123. }
  124. }

image.png
正确答案:80

2.作物杂交(dfs+dp)

image.png

  • 输入

    6 2 4 6 5 3 4 6 4 9 1 2 1 2 3 1 3 4 2 3 5 4 5 6

  • 输出

    16

1.思路分析

题目描述过于复杂,可能一开始并不能理解题目的意思。我们如何找杂交出最终作物的最少天数?
用动态规划的思路来说:设dp[T](T表示目标作物)表示杂交出目标作物的最少天数,这个dp[T]包含两个部分,杂交出目标作物的时间+种子成熟的时间。
然后我们来明确初始值,根据题目我们可以知道我们刚开始拥有哪些种子,所以当我们杂交目标种子所需要的一部分种子是刚开始拥有的,那么我们等待的天数就是0;所以第十一届蓝桥杯省赛(c组) - 图4
根据题意,我们可以知道杂交种子的方案,我们要在所有方案中找到一个天数最小的,那么大体思路就是暴力枚举,但是对于题目来说,暴力枚举的效率过低(两两种子都要枚举一遍,如果要经历多次的杂交,那么枚举的数量将成指数级增长)。所以核心思路是,反着枚举,即通过我们需要的目标种子来倒推我们需要的两个种子,再从我们需要的两个种子分别倒推出我们需要的另外两个种子。

2.代码

  1. import java.util.*;
  2. /*
  3. 创建一个内部类Fangan表示哪两个种子能杂交出另一个种子
  4. 首先获取输入,把我们拥有的种子标记为flag,dp[拥有的种子]=0;
  5. 创建dfs函数,返回值是杂交出目标种子所需要的最小天数:
  6. 如果该种子已拥有,返回dp[该种子]=0;
  7. 如果该种子没有拥有,那么
  8. dp[该种子]=min(dp[该种子],max(种子1成熟的时间,种子2成熟的时间)+max(dfs(种子1),dfs(种子2))
  9. */
  10. public class Main {
  11. public static int n,m,k,t,min=Integer.MAX_VALUE,sum1=0;
  12. public static int[] time;
  13. public static int[] dp;
  14. public static List<Fangan>[] res;
  15. public static boolean[] flag;
  16. public static void main(String[] args) {
  17. Scanner scanner=new Scanner(System.in);
  18. n=scanner.nextInt();//n种种子
  19. m=scanner.nextInt();//已有的种子数量
  20. k=scanner.nextInt();//可杂交的方案数
  21. t=scanner.nextInt();//目标种子编号
  22. res=new LinkedList[2015];
  23. dp=new int[n+1];
  24. Arrays.fill(dp,99999);
  25. flag=new boolean[n+1];
  26. flag[0]=true;
  27. time=new int[n+1];
  28. for (int i = 1; i <= n; i++) {//时间
  29. time[i]=scanner.nextInt();
  30. }
  31. for (int i = 0; i < m; i++) {//已有的种子
  32. int tmp= scanner.nextInt();
  33. flag[tmp]=true;
  34. dp[tmp]=0;
  35. }
  36. for (int i = 0; i < res.length; i++) {//方案
  37. res[i]=new LinkedList<>();
  38. }
  39. for (int i=0;i<k;i++){
  40. int x= scanner.nextInt();
  41. int y= scanner.nextInt();
  42. int tmp= scanner.nextInt();
  43. res[tmp].add(new Fangan(x,y));
  44. }
  45. System.out.println(dfs(t));
  46. }
  47. public static int dfs(int mubiao) {
  48. if(flag[mubiao]){
  49. return dp[mubiao];
  50. }
  51. for (int i = 0; i < res[mubiao].size(); i++) {
  52. Fangan tmp=res[mubiao].get(i);
  53. dp[mubiao]=Math.min(dp[mubiao],Math.max(time[tmp.x],time[tmp.y])+Math.max(dfs(tmp.x),dfs(tmp.y)));
  54. }
  55. flag[mubiao]=true;
  56. return dp[mubiao];
  57. }
  58. }
  59. class Fangan{
  60. int x;
  61. int y;
  62. public Fangan(int x,int y) {
  63. // TODO Auto-generated constructor stub
  64. this.x=x;
  65. this.y=y;
  66. }
  67. }