HNUST 手势密码

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xMaii/article/details/75315013

题目链接

题目描述
很多手机或平板电脑软件都可以设置手势密码,在设置了手势密码后,进入程序时,首先要输入手势密码。
手势密码最少选择4个点,最多选择9个点,理论上的密码组合总共有985824种,扣除掉其中不可能完成的组合(如一些点不允许绕过),最终的可能性是389112种。可见,手势密码加强了软件访问的安全性。
下面介绍一下手势密码的规则(如果你熟悉手势密码的规则,可略过):
1) 从某一个点出发,不间断地画线连接4-9个点,则从起点至终点构成的有序轨迹便构成一个有效的手势密码。
2) 在水平、垂直及对角方向上的3个点(假设依次为A点、B点、C点),在B点是未选点的情况下,A点是绕不过B点直接与C点相连的,如点1和点3直线连接绕不过点2。同样道理,点1和点9直线连接绕不过点5,等等。如图3所示,如果起点是1点,则手势密码必为1->2->3->6->5->4->7->8->9,而不可能是1->3->6->4->7->9。
3) 在连线不间断延展的过程中,只要还有未选的新点,就可以画线连接到该新点,而不管是否有重叠或是交叉,即经过已选点或已连线均可。例如,2->3->1->5或1->5->2->4都是允许的。
4) 在连线不间断延展的过程中,中间点是不允许经过2次的(除非是规则3说明的情况),终点虽然可以再连接到已选点,但却是无效的。例如,以1点为起点,则图4所示的手势密码跟图3所示的手势密码是一样的。

对于没研究过手势密码的同学,虽然我上面啰嗦了这么多,估计还是有疑惑的地方,干脆简单点说吧!
实际上,n个点能够构成的手势密码种数=n个点的排列总数-不可能完成的哪些排列数。

输入
有多行测试数据
每行包括2个数据:min和max,表示最少选择min个点,最多选择max个点。取值范围为4<=min<=max<=9。输入时min和max用空格隔开。

输出
与每行输入相对应,输出可以构成的手势密码总数。

样例输入
4 4
4 9

样例输出
1624
389112

该题的思路是求出分别以点1~9开头的所有手势密码的个数,可采用dfs进行搜索。不过,一开始我自己在做这道题时并没有很清晰的搜索思路,特别是不知如何处理如2->1->3这样的链接方式。最后在参考了网上的解法后恍然大悟。
关于如何处理譬如2->1->3这样的链接方式,我们可以这样做:首先创建一个类似于邻接矩阵的二维数组link[10][10],用它来表示任意两个点a,b之间是否能直接相连,如果能,记link[a][b] = link[b][a] = 0;如果不能,且一定是因为a和b的连线上穿过了某一点c,则记link[a][b] = link[b][a] = c。
我们再用一个一维数组vis[10]来记录某点是否已被访问,即是否被连接了。这样一来,我们在判断从点a出发是否能连接点b时需要判断这两个条件:首先是点b没有访问过,即vis[b] == 0;其次是link[a][b] ==0,即可以直接相连,或者是vis[link[a][b]] != 0,即如果a,b点之间的障碍点c = link[a][b]已经被访问过了,那么根据手势密码的规则,a,b点之间就可以直接相连了。
代码如下。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

int vis[10], link[10][10];
int Min, Max, sum;

void init()
{
    memset(link, 0, sizeof(link));
    link[1][3] = link[3][1] = 2;
    link[1][7] = link[7][1] = 4;
    link[1][9] = link[9][1] = 5;
    link[2][8] = link[8][2] = 5;
    link[3][7] = link[7][3] = 5;
    link[3][9] = link[9][3] = 6;
    link[4][6] = link[6][4] = 5;
    link[7][9] = link[9][7] = 8;
}

void dfs(int num, int cnt)
{
    if (cnt > Max) return;
    if (cnt >= Min&&cnt <= Max) sum++;
    for (int next = 1; next <= 9; next++)
    {
        if (!vis[next] && (!link[num][next] || vis[link[num][next]]))
        {
            vis[next] = 1;  
            dfs(next, cnt + 1);
            vis[next] = 0;
        }
    }
}

int main()
{
    init();
    while (scanf("%d %d", &Min, &Max) != EOF)
    {   
        sum = 0;
        for (int num = 1; num <= 9; num++)
        {
            memset(vis, 0, sizeof(vis));
            vis[num] = 1;
            dfs(num, 1);
        }
        printf("%d\n", sum);
    }
    //system("pause");
    return 0;
}

另外,我在第一次提交时显示编译错误,如下图。
这里写图片描述
意思是变量max有歧义。后来一想,应该是OJ可能把max当作max()函数了,所以提示编译错误。故将max和min均改成Max和Min即可。

猜你喜欢

转载自blog.csdn.net/xMaii/article/details/75315013