小蓝要用七段码数码管来表示一种特殊的文字。
上图给出了七段码数码管的一个图示,数码管中一共有 7 段可以发光的二
极管,分别标记为 a, b, c, d, e, f, g。
小蓝要选择一部分二极管(至少要有一个)发光来表达字符。在设计字符
的表达时,要求所有发光的二极管是连成一片的。
例如:b 发光,其他二极管不发光可以用来表达一种字符。
例如:c 发光,其他二极管不发光可以用来表达一种字符。
这种方案与上一行的方案可以用来表示不同的字符,尽管看上去比较相似。
例如:a, b, c, d, e 发光,f, g 不发光可以用来表达一种字符。
例如:b, f 发光,其他二极管不发光则不能用来表达一种字符,因为发光
的二极管没有连成一片。
思路:
7个灯管共有127种情况。用二进制表示即从0000000到1111111。
每一位二进制分别表示每个灯管的亮灭情况。(0表示灭,1表示亮)
将这127种二进制数与连通图相结合进行判断得出符合要求的灯管并计数。
127种二进制数用来判断灯的亮灭。
连通图在上一个条件下用来判断亮的灯是否连通。
先上代码,后面根据代码进行讲解
//全局变量
static int count=0;//计数
static int[][] m= {//连通矩阵
{0,1,0,0,0,1,0},
{1,0,1,0,0,0,1},
{0,1,0,1,0,0,1},
{0,0,1,0,1,0,0},
{0,0,0,1,0,1,1},
{1,0,0,0,1,0,1},
{0,1,1,0,1,1,0}
};
static char[] a;//存储127种情况的二进制字符数组
static boolean flag;//用来标记符合要求的情况
public static void main(String[] args) {
for (int j = 1; j < 128; j++) {//对127种情况进行循环
// 获取127种情况的二进制,并取反(a[0]表示灯管0,以此类推a[6]表示灯管6)与连通图首位对应
StringBuffer s=new StringBuffer(Integer.toBinaryString(j)).reverse();
a=s.toString().toCharArray();//将二进制数转为字符数组
int[] f={1,1,1,1,1,1,1};//将每个灯管标记为“未遍历”
flag=true;//初始化标记
for(int i=0;i<a.length;i++){//循环二进制找到第一个亮灯的
f[i]=0;//未亮灯且标记为被遍历过
if(a[i]=='1')
{
dfs(f,i);//以第一个亮灯的灯管为开始遍历所有灯管
break;
}
}
if(flag==false)count++;//符合要求则计数
}
System.out.println(count);//结果输出
}
public static void dfs(int[] f,int k){//k表示亮灯的灯管序号
f[k]=0;//标记为遍历过
for(int i=0;i<a.length;i++){在连通图中第k行寻找连通且亮灯的灯管进行遍历
if(m[k][i]==1&&a[i]=='1'){//连通且亮着
if(f[i]==1)//未被遍历过
dfs(f,i);//寻找下一个
}
}
for(int i=0;i<a.length;i++){//存在未被连通的,即无法连通的
if(f[i]==1&&a[i]!='0')//存在亮灯但不连通的灯管
return;//返回上一层
}
flag=false;//每个灯管被遍历且亮灯的灯管是连通的
return;//返回上一层
}
步骤:
对127种情况进行二进制表示
将二进制数反转,使其下标与数码管序号一一对应
将二进制数每一位标记为未被遍历。f={1,1,1,1,1,1,1}1表示未被遍历,0表示已遍历
找到二进制中第一个'1'(第一个亮灯的灯管)对其进行遍历,并将其标记为已遍历
通过dfs()方法在连通图第k行寻找连通且亮灯的灯管,对其再进行遍历,并将其标记为已遍历过(f[k]=0)
直到遍历完二进制每一位数
循环判断数组f是否存在未遍历或遍历了但灯是灭的情况。(是否存在f[i]==1&&a[i]!='0')如果存在则表示不连通,不存在表示连通,并标记flag=false再递归返回上一层。
如果7个灯管都遍历完了且均符合要求,则计数+1
循环下一种情况(循环步骤1)
举例:
1、a=101010,a'=010101(表示1、3、5号灯管灯亮,其余灯灭)
找到第一个亮灯灯管下标为1。调用dfs(f,1)。(此时f={0,0,1,1,1,1,1})
在连通图第k行(k=1)寻找可连通的灯管
按顺序发现与1号灯管连通的有0号灯管,由于f[0]=0已被遍历过,继续寻找。
2号灯管也连通,判断a[2]='0'灯是灭的,继续寻找。
由于a的长度为6,a[6]='0'(第7个灯管是灭的),不用考虑,直接跳过。
由于循环7个灯管后均未找到与第一个灯管相连且亮灯的灯管,于是进入下一个循环。
对每个二进制标记进行判断(f={0,0,1,1,1,1,1})
发现在查询过连通情况后仍然有亮灯的未被遍历,表明亮灯的灯管不连通,则不计数直接退出函数。
进入下一种情况继续判断。
2、a=1111000,a'=0001111(表示3、4、5、6号灯管是亮的)
找到第一个亮灯的灯管序号为3。调用dfs(f,3)。(此时f={0,0,0,0,1,1,1})
在连通图第k行(k=3)寻找可连通的灯管
按顺序发现2号管可连通,由于f[2]=0,表示已被遍历过。继续寻找
发现4号灯管也可连通,同时f[4]=1表示未遍历,a[4]='1'表示亮灯,则调用dfs(4),同时将4号灯管标记为已遍历过f[4]=0。
查找连通图第4行,发现3号灯管可连通,由于f[3]=0已被遍历过,继续寻找
发现5号灯管也可连通,f[5]=1表示未遍历,a[5]='1'表示灯亮,则调用dfs(5),同时将5号灯管标记为已遍历过f[5]=0。
查找连通图第5行,发现0号灯管可连通,由于f[0]=0已被遍历过,继续寻找
发现4号灯管可连通,由于f[4]=0已被遍历过,继续寻找
发现6号灯管也可连通,f[6]=1表示未遍历,a[6]='1'表示灯亮,则调用dfs(6),同时将6号灯管标记为已遍历过f[6]=0。
遍历连通图第6行,由于f[0..6]=0表示所有灯管都被遍历过,直接递归返回上一层即dfs(5)执行结束后的位置。
循环判断数组f发现都已遍历,则flag=false并递归返回dfs(4)的结束位置。
继续循环第4行发现6号灯管也可连通,由于f[6]=0表示已遍历,则退出循环。
循环判断数组f发现都已遍历,则flag=false并递归返回dfs(3)的结束位置。
继续循环第3行,发现5,6号灯管与3号灯管不连通,退出循环。
循环判断数组f发现都已遍历,则flag=false并递归返回。由于dfs(3)是第一个递归函数,递归返回就是直接结束主函数中的dfs()方法。
发现flag=false,对count+1
进入下一种情况继续判断。