上一题:蓝桥杯2020年真题演练——3、蛇形填数(JavaA组)
题目描述
答案:80
题目解析
⭐⭐把七个发光二极管看作七个顶点,是否相邻视作是否有边,则该问题就转化为一个和图相关的问题了。
转化结果如图:
⭐则题中所要求的发光管必须连成一片对应在图中就是相关的顶点必须是连通的,这样我们就可以通过图的搜索来检查发光灯管是否连成一片。
⭐另外七个灯管一共有27种不同的情况,依次枚举检查是否符合条件即可。
⭐这道题中我们用邻接矩阵存储图,代码中第一部分首先将图存到邻接矩阵matrix中
⭐图存储好后是一个for循环依次遍历27中情况,部分小伙伴可能不太理解(1<<7)是个什么东西,这个其实就是27,不过它是通过1左移7位(二进制下)得到的。不太懂的可以区查一下移位运算符。
⭐每次循环调用check(i)方法,若符合题意,返回true并使ans加一,否则不做任何操作
这时候又会有小伙伴不理解了,为什么只有一个参数i?其实我们是通过数字i在二进制下各位上的0和1来标识对应的灯有没有亮,所以一个数字就代表一个组合,0到27正好包含了所有的可能情况。
举个例子:14,二进制表示为:
0 | 0 | 0 | 1 | 1 | 1 | 0 |
---|
就代表b、c、d三个灯亮,其它灯不亮
⭐进入check()函数后首先初始化flash数组和visted数组,分别初始化位“不亮”,“未访问”
⭐分析参数x二进制下各位上的数字是0还是1,若是1则对应的灯要“点亮”,即flash[i]=1
这里也用到了位运算的技巧和“&”运算符,是一些基础知识,不了解的可以先去学一下,实在不理解的欢迎评论区留言。
⭐check函数的最后一部就是判断点亮的灯是否能够连在一起,具体分析如下:
count初始为0,然后依次对未点亮且还没有被访问的顶点进行广度优先搜索并且count+1,接下来是关键,如果只进行了一次搜索,所有点亮的点已经都被访问了,那么说明这个组合就是连通的,而且既然所有点亮的顶点已经都被访问了,所有的点都不符合if条件,就不会再进行第二次搜索了,所以count=1。
相反如果一次搜索无法遍历所有的点亮的点,则说明这些点不连通,将进行不止一次搜索,所以count>1
最后返回 count==1。
实际上这个方法就是计算一个图有多少个连通子图的方法
⭐最后就是广度优先搜索了,也简单分析一下吧
广度优先搜索中我们从初始节点开始,依次搜索该节点的所有还未访问的节点(本题中还要加一个条件,“点亮”),同时被搜索的节点也要依次搜索其所有的邻接节点,直到图中所有顶点都被访问(这里所说的是连通图)
具体的代码实现思路需要用到队列,步骤如下:
1、首先将初始节点放入队列中,并标记已被查看
2、然后只要队列不为空,就要进行循环,将对头的相邻节点且是点亮的节点、未被访问的节点,放入队列中,并把刚放入的节点标记为以访问。这样对头的节点及其相邻节点就都被访问了所以将对头节点从队列中去除。
3、当队列为空时说明所有节点都已被访问。
这里描述比较简单,不懂的建议再去看看视频教学
源代码
import java.util.*;
public class Main {
private static int ans;
private static int[][] matrix=new int[7][7];
private static int[] flash=new int[7]; //亮不亮
private static int[] visted=new int[7]; //有没有被访问
public static void main(String[] args) {
//用图存储七段码
matrix[0][1]=matrix[1][0]=1;
matrix[0][5]=matrix[5][0]=1;
matrix[5][6]=matrix[6][5]=1;
matrix[6][1]=matrix[1][6]=1;
matrix[5][4]=matrix[4][5]=1;
matrix[4][6]=matrix[6][4]=1;
matrix[6][2]=matrix[2][6]=1;
matrix[1][2]=matrix[2][1]=1;
matrix[4][3]=matrix[3][4]=1;
matrix[2][3]=matrix[3][2]=1;
//用i的二进制位上的0和1表示对应位置的灯亮或不亮(0<=i<=2的七次方)
for (int i=0;i<(1<<7);i++){
//依次遍历2的七次方种情况,检查对应情况下的图是否连通
if(check(i)){
ans++;
}
}
System.out.println(ans);
}
public static boolean check(int x){
for (int i = 0; i <= 6; i++) {
flash[i] = visted[i] = 0;
}
//根据x点亮对应的灯
for (int i = 0; i <=6; i++) {
if(((x>>i)&1)!=0){
flash[i]=1;
}
}
//
int count=0;
for (int i = 0; i <=6; i++) {
if(flash[i]==1&&visted[i]==0){
bfs(i);
count++;
}
}
return count==1;
}
//广度优先遍历(只会试图去遍历点亮的点)
public static void bfs(int i){
LinkedList<Integer> que=new LinkedList<>();
que.offer(i);
visted[i]=1;
while(!que.isEmpty()){
int u=que.peek();
que.poll();
for (int j = 0; j <=6 ; j++) {
if(matrix[u][j]!=0&&flash[j]==1&&visted[j]==0){
visted[j]=1;
que.offer(j);
}
}
}
}
}