链接:牛客网
来源:牛客网
栗主席(lizi)是某xxxx大学的一个不得了的程序猿,然而没想到吧,他竟然有女盆友,我们假设为QAQ!!!
那天,QAQ问栗子:你的小米5s的图像解锁密码到底是多少?
栗子:嘛?我仔细想想...
QAQ:你仿佛在逗我...
...
栗子:我的图像解锁用过好多次密码,后来都是用指纹解锁,所以忘记密码辣。但是我记得可能是那几个密码
QAQ:那你务必告诉我...
栗子: ...
然后,栗子就写下了一堆可能的密码,安卓图案解锁中,数字对应的位置已经标出。
但是栗子当然不想把真正的密码告诉QAQ,所以给QAQ的一系列的密码中,甚至有一些密码,是不符合安卓图案解锁的规则的。
QAQ也知道栗子肯定不老实,给了很多错的密码,甚至不符合规则的密码,所以想请你来找出,哪些密码是不符合规则的。
安卓图案解锁的密码有这样的一些特点:
1.每个数字最多只会被使用一次。
2.如果想直接连接两个数字,但是线段中会经过另一个数字,当且仅有那个数字已经在之前就被使用过了,才会合法。(比如你想从1直接连接到9,那么要么是1->5->9,要么是5在之前已经被使用过了,然后才能直接从1->9)
输入描述
多组输入 每组输入占一行,包含一串数字(1~9),长度不超过30(这里千万千万别忽略!!!)
输出描述
输出这个安卓图案解锁是否合法,如果合法输出"YES",反之输出"NO" (请参照样例输出,不要输出引号)
示例
输入:
14569
1953
15963
15953
输出:
YES
NO
YES
NO
细细读题~
首先,题目很长,因此,我们要把它简化,留下最重要的:
题目输出会给出很多的字符串,有正确的,有错误的,我们只需要判断是否满足解锁要求就好~
那么解锁要求有哪些呢?
题目有直接给出的:1. 每个数字只能使用一次;2. 直接相连的两个数字,若中间会经历其他数字,那么该数字必须是使用过的。(只看文字不好理解,可以对照上面的图走一遍)
我走过的弯路/\
最开始其实想了很多的思路:想要二维数组来存储9个数字,但是我不知道如何去判断它们各自所在的位置。所以我放弃了这个思路,但是后边拜读了大佬的代码,稍微懂了一点点(代码会附在后边)。
后来又想了好久,就觉得这题一定存在着某种规律,只是我还没找到罢了。然后,看着图去找规律,慢慢摸清了一点东西:
只要两个数字相加为10,只需要判断5是不是为空即可;
或者,两个奇数相加,判断这两个数字相加后除2的数是不是为空即可。
然后不断地修改,到最后发现,我判断的逻辑不完整(或者说,逻辑有些混乱)!!!哪怕,通过了样例,提交的通过率依旧为0%。
后来,第二天重新整理了一下思路,发现:
我只需要重点判断 是否会出现重复数字的情况 和 两个相连的数字中间是会穿过一个数字的情况 即可!
是否会出现重复数字,其实在遍历字符串的时候就可以判断了,所以,接下来可以重点看后半段。
两个数会穿过5的是一种情况,除了5,其他数字都有可能穿过5,判断会有哪两个数会穿过5,只需要两个数字相加整除2就好;
还有一种情况是无需穿过5(自然也就不会经过5),且相连的数字,且会穿过一个数字:四周这个框!有个很明显的规律就是,想要满足前提要求,相连的两个数必须同时是奇数。
ok,思路已经理清,接下来就是实现代码~
代码的实现~
#include<stdio.h>
// #include<stdlib.h>
//包含了转int类型的atoi函数
void judge(char pw[])
{
int r = 0, arr[9]={0}, q[33];
//r为当前已经使用的数字个数,arr标记是否为空,q表示数字经历的顺序
int flag1 = 1;//判断是否有重复数字
int len = strlen(pw);//len表示pw的字符串长度
for(int i = 0;i < len; i++)
{
int elem = pw[i] - '0';
// int elem = atoi(pw[i]);
// 错误用法!!!atoi函数适用于将字符串转换为 int 类型,而不是单个字符
q[r++] = elem;
if(!arr[elem-1])
{
arr[elem-1] = 1;//标记哪些数字是用过的
}
else//发现有重复的数字,直接say no
{
flag1 = 0;
printf("NO\n");
break;
}
}
if(!flag1)
return;
int flag3 = 0;
//flag2判断是否找到中间数字,flag3判断密码是否正确
for(int i = 0;i < r - 1; i++)//直接遍历密码
{
int j = i + 1;//i,j为两个相连的数字
int flag2 = 0;//一定,一定要记得重置啊啊啊!!!!
if(q[i]+q[j] == 10)//都经过5的走法(1-5-9,3-5-7,4-5-6,2-5-8)
{
for(int s = 0; s < i; s++)//遍历在当前数字之前,已经经过的数字
{
if(q[s] == 5)
{
flag2 = 1;
break;
}
}
if(!flag2)
{
flag3 = 1;//未经过5,因此直接错误
break;
}
}
else if(q[i]%2!=0&&q[j]%2!=0&&q[i]!=5&&q[j]!=5)
//不经过5,但是两个相连数字之间是有数字的
{
for(int s = 0; s < i; s++)//遍历在当前数字之前,已经经过的数字
{
if(q[s] == (q[i]+q[j])/2)
{
flag2 = 1;
break;
}
}
if(!flag2)
{
flag3 = 1;
break;
}
}
}
if(flag3)
printf("NO\n");
else
printf("YES\n");
return;
}
int main()
{
char pw[33];
while(scanf("%s",pw)!=EOF)
{
judge(pw);
}
return 0;
}
大佬的代码@@
在此感谢大佬的指导!!!
// 按模拟题写法,就是模拟题目所要求的过程。比如这道题,就是模拟你解锁屏幕的过程。
// 你想象一下,你解锁屏幕的时候,是咋样判断的。
#include<stdio.h>
#include<string.h>
int point[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int dic[10] = {0};
char s[35];
int check(int mid_pos_x, int mid_pos_y)
{
int mid_num = mid_pos_y*3+1+mid_pos_x%4; // 还原中间数字
if(dic[mid_num])
return 0; // 通过
else
return 1; // 未通过
}
int main()
{
while(~scanf("%s", s))
{
// 初始化标记用过数字的字典
for(int i=0; i<10; i++)
dic[i] = 0;
int len = strlen(s);
int invalid = 0; // 判断
int last = 0; // 上一个数
for(int i=0; i<len; i++)
{
int num = s[i] - '0';
// 已经走过就直接淘汰
if(dic[num])
{
invalid = 1;
break;
}
else
{
// 第一个数字
if(last==0)
{
dic[num] = 1;
last = num;
continue;
}
// 将数字转换为九宫格坐标
int pos_y = (num-1)/3;
int pos_x = (num-1)%3;
// 上一个数字的坐标
int last_pos_y = (last-1)/3;
int last_pos_x = (last-1)%3;
// 判断是否是未直接相连的数字
if (pos_x-2==last_pos_x && pos_y==last_pos_y && check(pos_x-1, pos_y)) // 左边不相连情况
{
invalid = 1;
break;
}
else if(pos_x==last_pos_x && pos_y-2==last_pos_y && check(pos_x, pos_y-1)) // 上边不相连情况
{
invalid = 1;
break;
}
else if (pos_x==last_pos_x && pos_y+2==last_pos_y && check(pos_x, pos_y+1)) // 下边不相连情况
{
invalid = 1;
break;
}
else if(pos_x+2==last_pos_x && pos_y==last_pos_y && check(pos_x+1, pos_y)) // 右边不相连情况
{
invalid = 1;
break;
}
else if (pos_x+2==last_pos_x && pos_y+2==last_pos_y && check(pos_x+1, pos_y+1)) // 右下角不相连情况
{
invalid = 1;
break;
}
else if (pos_x+2==last_pos_x && pos_y-2==last_pos_y && check(pos_x+1, pos_y-1)) // 右上角不相连情况
{
invalid = 1;
break;
}
else if (pos_x-2==last_pos_x && pos_y+2==last_pos_y && check(pos_x-1, pos_y+1)) // 左下角不相连情况
{
invalid = 1;
break;
}
else if (pos_x-2==last_pos_x && pos_y-2==last_pos_y && check(pos_x-1, pos_y-1)) // 左上角不相连情况
{
invalid = 1;
break;
}
// 标记,表示已经走过
dic[num] = 1;
// 更新上一个数字
last = num;
}
}
if(invalid)
puts("NO");
else
puts("YES");
}
return 0;
}
代码的对比,首先,给我最直观的感受就是变量的命名不是随意的,每个命名都有它的含义!这样写出来的代码,一看就比较规范,整齐。
其次,前面不是说,我不会判断数字的位置在哪嘛,这个大佬他用的坐标转换的公式吧,类似的四行四列就int last_pos_y = (last-1)/4;
int last_pos_x = (last-1)%4;(以题目中的为例)
最后,关于模拟题嘛,我感觉我还能还是get不到什么的东西,所以还是得继续刷题!