您的位置:首页 > 其它

手机九宫格滑锁密码的所有密码组合计算

2013-10-10 15:01 288 查看
题目好像是哪个公司的笔试题,同学给我的,然后自己就试着写写,才学浅陋,花了蛮多时间写的

题目大致意思如下:


                 


手机九宫格解锁图案如上,假设把一次先行后列标记九个圆一次为1,2,3,4,5,6,7,8,9这九个数字,求所有合法的密码情况

合法的密码我们假设要求如下:

1、假设密码的长度至少为2,最长当然为9

2、密码中不能有重复的数字出现,比如不能同时出现两个2

3、还有就是密码相邻的数字必须在图形上是相连的,这样才符合手的滑动

      比如1之后只能是2、4、5三种可能,2之后只能是1、3、4、5、6五种可能情况,也就是每个数最多有八种下一步的走法

现在就是把所有的情况罗列出来

解法:解法可能不是很高效,主要使用了深度遍历算法,假设先以1作为开始点,使用深度遍历,每次都判断当前数的八个方向,如果有某一个方向上的点没有超出边界

            那就先遍历那个点,这样深度遍历下去。

            最主要的问题就是怎么记录下已经遍历的路线,因为要输出所有的情况,这样必须时刻记录当前遍历的路线。

            还有就是已经遍历的点需要做标记才能避免后面不会遍历重复的点情况出现。这也是一个难点之一。开始的时候想过使用一个二维数组作为标记,但是后来觉得不对,

假设我们先开始遍历1,那之后遍历2,此时如果用全部二维数组标记的话,也就是1和2都已经标记走过了。那在2的情况下深度遍历下去,后面肯定会回退回来到点1的另外

的方向,比如说4,此时遍历的顺序为1、4,也就是在这种顺序下的话2是没有遍历的,但是用全局二维数组就是前面已经标记了,这样的话后面就混乱了,所以用这样的方法不行。

             后来想了方法用链表记录已经遍历的点,因为是深度遍历,每次都是通过不断的递归调用遍历,在递归中使用for判断八个方向,没有遍历则递归下去。

             每次把在判断方向中符合的点插入链表末尾,然后递归遍历该点,遍历时候判断链表长度符合要求则为一种情况并输出。

             每次插入到链表末尾的时候都会用指针指向末尾的结点,这样在递归倒退的时候,也就是换另一条线路,也就是此时末尾的结点要重新改为没有遍历过的,所以递归返回时把链表末尾的结点删除。

              同时在结点信息中记录step,记录此时是第几个结点,也就是遍历的长度。

代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<fstream.h>
#include<iomanip.h>

struct node {   // 记录遍历的数字链表
int num;    // 记录当前链表结点数字
int step;   // 从表头到当前点的步数
struct node *next;  // 指向下一个结点
};

struct load {  // 方向结点(x,y)
int x;
int y;
};

load dir[8] = {{0,-1},{-1,-1},{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1}};  // 记录八个方向数组

int map[3][3] = {{{1},{2},{3}},{{4},{5},{6}},{{7},{8},{9}}};  // 记录地图数字

node *Linklist;  // 记录已经走过链表指针
node *end;       // 每次指向链表的结尾,即记住当前已走的最后一个数字

void Traversal() {   // 遍历当前聊表,即输出一种情况
node *p;
p = Linklist;
while(p->next) {
printf("%d --> " , p->num);
p = p->next;
}
printf("%d\n",p->num);
}

/*
控制台显示不下,所以同时在D盘文件中写了一份,
D盘的code.txt文件中
*/
void Traversal2() {
node *p;
p = Linklist;
ofstream fout;
fout.open("d:\\code.txt",ios::app);
if(!fout) {
return ;
}
while(p->next) {
fout<<p->num<<"-->";
cout<<p->num<<"-->";
p = p->next;
}
fout<<p->num<<endl;
cout<<p->num<<endl;
fout.close();
}

int IsSearch(load * temp) {  // 查询当前点是否被遍历过,已经记录在链表中则遍历
node * p;
p = Linklist;
while(p) {
if(p->num == map[temp->x][temp->y]) {
return 0; // 已经遍历过
}
p = p->next;
}
return 1;         // 没有遍历过
}

void DfsSearch(int x , int y) {
int i;
load *temp = (load*)malloc(sizeof(load));
node *p , *q;
// 判断当前点的八个方向的下一步坐标是否符合
for(i = 0 ; i < 8 ; i++) {
temp->x = x + dir[i].x;
temp->y = y + dir[i].y;
// 下一步的点是否还在map范围之内
if((temp->x) >=0 && (temp->x) <=2 && (temp->y) >= 0 && (temp->y) <=2) {
// 下一步的点是否已经走过
if( IsSearch(temp) ) {
// 没有遍历过则新建结点加入链表末尾,并遍历改点
p = (node*)malloc(sizeof(node));
p->num = map[temp->x][temp->y];
p->step = end->step + 1;
p->next = end->next;
end->next = p;
q = end;
end = p;
// 当前链表是否已经长度>=2,>=2则符合要求,遍历输出
if((end->step) >= 2) {
// Traversal();
Traversal2();
}
// 以当前点为基准,使用深度遍历搜索
DfsSearch(temp->x , temp->y);
/*
退出递归,即以当前链表末尾点的情况都已经遍历
则删除当前链表末尾点,即删除末尾点的标记
*/
end = q;
end->next = NULL;
free(p);
} else {
// 当前点已经遍历
}
}
}
free(temp);
// 当前点的八个方向都已经遍历,则回退
}

int main() {
node * p = (node*)malloc(sizeof(node));
Linklist = NULL;
int i , j , num = 1;
// map地图的每个点开始为起点深度遍历搜索
for(i = 0 ; i < 3 ; i++) {
for(j = 0;j < 3; j++) {
p->num = map[i][j];
p->step = 1;
p->next = NULL;
// 第一个点作为链表的结点
Linklist = p;
end = Linklist;
// 进行深度遍历
DfsSearch(i , j);
}
}
free(p);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息