5.8 安卓图案解锁

链接:牛客网
来源:牛客网

栗主席(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不到什么的东西,所以还是得继续刷题!

  • 8
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值