2018网易互娱笔试题-手势锁

 

题目描述

在移动应用开发中,手势锁是一种常见的保护用户数据安全的手段。

现在小明也参与到一个新型手势锁的开发组中,负责开发其中的手势判断模块。这个新型的手势锁是通过用户在3*3的点阵界面上,通过连结点阵中的点,绘制出的图案确定的。用户在设置锁时,先绘制一个图形,在解锁时,只要绘制完全一致的图形,即可解锁成功。一个典形的手势锁如下图。

2018网易互娱笔试题-手势锁_第1张图片

为方便描述,我们给阵中的点进行如下的编号。

2018网易互娱笔试题-手势锁_第2张图片

以下是用户绘制手势锁图形的过程:

1. 选择一个点,并用手指触摸屏幕该点;

2. 执行步骤3一次或以上,然后执行步骤4;

3. 选择另一个点,并在手指不离开屏幕的情况下,移动到该点,这样视作手指移动前的点与手指移动后的点之间有一条连线;

4. 手指离开屏幕,绘制图形完成。

 2018网易互娱笔试题-手势锁_第3张图片

绘制手势锁图形的过程中有以下规则:

1. 至少选择两个点;

2. 同一个点,不能选择两次或以上;

3. 连线可以交叉或重叠;

4. 移动手指时,中间可以绕过其它点,例如下图中,可以把手指从(0, 0)直接移动到(0, 2),而无需选择(0, 1)

判断两个手势图形,有以下规则:

1. 图形不能旋转、平移以及缩放。例如图形(0, 0) -> (0,1)(1, 0) -> (1, 1)是两种不同的图形,因为图形不能平移;

2. 判断依据为图形中的连线形状,不包括图形中的点。例如图形(0, 0) -> (0,2)以及(0, 0) -> (0, 1) -> (0, 2)的连线形状是一样的,选择的点不一样,依然可以看作同一种图形;

3. 判断依据为图形中的连线形状,不包括连线的方向。例如图形(0, 0) -> (0,2)以及(0, 2) -> (0, 0)的连线形状是一样的,方向是相反的,依然可以看作同一种图形;

4. 重叠的连线并不影响图形的形状。例如图形(0, 0) -> (0,2)以及(0, 0) -> (0, 2) -> (0, 1),虽然其中第二个图形中的(0, 0) -> (0, 2)(0, 2) -> (0,1)有重叠部分,但重叠的部分不影响图形形状,依然可以看作同一种图形;

在以上规则中,能绘制出和下图相同图形的手势包括但不限于:

(0, 0) -> (0, 2) -> (0,1) -> (2, 1)

(1, 1) -> (2, 1) -> (0, 1) ->(0, 2) -> (0, 0)

(0, 2) -> (0, 0) -> (0, 1) ->(1, 1) -> (2, 1)

2018网易互娱笔试题-手势锁_第4张图片

现在为了验证该手势锁的安全性,需要计算出总共能绘制出多少种不同的图形。而在多次独立测试中,可能把点阵中的某些点去掉(即不能被选择)。

输入描述:

输入的第一行为一个正整数T,表示测试数据组数。
接下来有T组数据。每组数据有3行。每一行为一个长度为3的字符串,字符串中只包含字符'.''X',表示当前要测试的点阵,'.'表示该点可以被选择,'X'表示该点不可以被选择。
数据保证至少有两个点可以选择。
数据范围:
对于所有数据,都满足1<=T<=10

输出描述:

对于每一组数据,输出一行,包含一个整数,为在给定的点阵中可以绘制的不同的图形数量。

示例1

输入

3

...

XXX

XXX

...

XXX

X.X

.X.

X.X

.X.

输出

3

22

111

分析:

题目中最困难的是如何判断两个手势绘制出来的图像是否相同。我们用字符串来表示绘制的图像,将各个点编号,一条线段两端的序号分别作为十位和个位,将字符串对应下标出设为1,表示存在该线段。

解题思路:

①根据输入获取所有的可用点;

②排列出所有可用点的子序列(大小至少为2);

③画出子序列的图像,并比较是否已经存在相同的图像。

手势图像表达方式:

①将各个点按照如下顺序编号

2018网易互娱笔试题-手势锁_第5张图片

②每条线段的两个顶点中,序号小的作为十位,大的作为个位,将字符串中对应的字符设为1

    比如0,1顶点相连,得到:01000 00000 00000 ...

        1,2顶点相连,得到:00000 00000 01000 ...

③从图中可以看出,部分线段会跨过一个中间点,如0和8之间跨过了4,我们做出规定,将这样的线段分成两段,一共有8条这样的线段:

 

横向
0->2 0->1->2
3->5 3->4->5
6->8 6->7->8
纵向
0->6 0->3->6
1->7 1->4->7
2->8 2->5->8
斜向
0->8 0->4->8
2->6 2->4->6

部分代码:

生成手势图像(字符串):

string int2str(int index1, int index2)
{
	if (index2 < index1)
		swap(index1, index2);
	string tmpstr(100, '0');
	// xiexian
	if (index1 == 0 && index2 == 8)
	{
		tmpstr[4] = '1';
		tmpstr[48] = '1';
		return tmpstr;
	}
	if (index1 == 2 && index2 == 6)
	{
		tmpstr[24] = '1';
		tmpstr[46] = '1';
		return tmpstr;
	}
	// hengxian
	if (index1 == 0 && index2 == 2)
	{
		tmpstr[1] = '1';
		tmpstr[12] = '1';
		return tmpstr;
	}
	if (index1 == 3 && index2 == 5)
	{
		tmpstr[34] = '1';
		tmpstr[45] = '1';
		return tmpstr;
	}
	if (index1 == 6 && index2 == 8)
	{
		tmpstr[67] = '1';
		tmpstr[78] = '1';
		return tmpstr;
	}
	// shuxian
	if (index1 == 0 && index2 == 6)
	{
		tmpstr[3] = '1';
		tmpstr[36] = '1';
		return tmpstr;
	}
	if (index1 == 1 && index2 == 7)
	{
		tmpstr[14] = '1';
		tmpstr[47] = '1';
		return tmpstr;
	}
	if (index1 == 2 && index2 == 8)
	{
		tmpstr[25] = '1';
		tmpstr[58] = '1';
		return tmpstr;
	}
	tmpstr[index1 * 10 + index2] = '1';
	return tmpstr;
}

合并手势图像(字符串合并):

string combine(string & str, string tmp)
{
	string res;
	for (int i = 0; i < 100; i++)
	{
		if (tmp[i] == '1' || str[i] == '1')
			res.push_back('1');
		else
			res.push_back('0');
	}
	return res;
}

完整代码:

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

string int2str(int index1, int index2)
{
	if (index2 < index1)
		swap(index1, index2);
	string tmpstr(100, '0');
	// xiexian
	if (index1 == 0 && index2 == 8)
	{
		tmpstr[4] = '1';
		tmpstr[48] = '1';
		return tmpstr;
	}
	if (index1 == 2 && index2 == 6)
	{
		tmpstr[24] = '1';
		tmpstr[46] = '1';
		return tmpstr;
	}
	// hengxian
	if (index1 == 0 && index2 == 2)
	{
		tmpstr[1] = '1';
		tmpstr[12] = '1';
		return tmpstr;
	}
	if (index1 == 3 && index2 == 5)
	{
		tmpstr[34] = '1';
		tmpstr[45] = '1';
		return tmpstr;
	}
	if (index1 == 6 && index2 == 8)
	{
		tmpstr[67] = '1';
		tmpstr[78] = '1';
		return tmpstr;
	}
	// shuxian
	if (index1 == 0 && index2 == 6)
	{
		tmpstr[3] = '1';
		tmpstr[36] = '1';
		return tmpstr;
	}
	if (index1 == 1 && index2 == 7)
	{
		tmpstr[14] = '1';
		tmpstr[47] = '1';
		return tmpstr;
	}
	if (index1 == 2 && index2 == 8)
	{
		tmpstr[25] = '1';
		tmpstr[58] = '1';
		return tmpstr;
	}
	tmpstr[index1 * 10 + index2] = '1';
	return tmpstr;
}

string combine(string & str, string tmp)
{
	string res;
	for (int i = 0; i < 100; i++)
	{
		if (tmp[i] == '1' || str[i] == '1')
			res.push_back('1');
		else
			res.push_back('0');
	}
	return res;
}

int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		vector points;
		string str;
		cin >> str;
		if (str[0] == '.')
			points.push_back(0);
		if (str[1] == '.')
			points.push_back(1);
		if (str[2] == '.')
			points.push_back(2);
		cin >> str;
		if (str[0] == '.')
			points.push_back(3);
		if (str[1] == '.')
			points.push_back(4);
		if (str[2] == '.')
			points.push_back(5);
		cin >> str;
		if (str[0] == '.')
			points.push_back(6);
		if (str[1] == '.')
			points.push_back(7);
		if (str[2] == '.')
			points.push_back(8);

		set allimg;

		for (size_t i = 2; i <= points.size(); i++)
		{
			sort(points.begin(), points.end());
			vector last;
			do 
			{
				bool deal = false;
				if (last.size() == 0)
				{
					deal = true;
					for (int j = 0; j < i; j++)
					{
						last.push_back(points[j]);
					}
				}
				else
				{
					for (int j = 0; j < i; j++)
					{
						if (last[j] != points[j])
						{
							deal = true;
							break;
						}
					}
				}
				if (deal)
				{
					for (int j = 0; j < i; j++)
					{
						last[j] = points[j];
					}
					string tmpimg = int2str(points[0], points[1]);
					for (int j = 2; j < i; j++)
					{
						string tttt = int2str(points[j - 1], points[j]);
						tmpimg = combine(tmpimg, tttt);
					}
					allimg.insert(tmpimg);
				}
			} 
			while (next_permutation(points.begin(),points.end()));
		}
		cout << allimg.size() << endl;
	}

	return 0;
}

完整代码修改版(子串全排列)

代码中还有一些可以优化的地方:

在寻找所有的连接顶点的顺序的时候(即所有子串的全排列),使用了stl的next_permutation函数,导致了多层重复求解所有顶点的全排列。

修改了第140到182行。

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

vector points;
set allimg;

string int2str(int index1, int index2)
{
	if (index2 < index1)
		swap(index1, index2);
	string tmpstr(100, '0');
	// xiexian
	if (index1 == 0 && index2 == 8)
	{
		tmpstr[4] = '1';
		tmpstr[48] = '1';
		return tmpstr;
	}
	if (index1 == 2 && index2 == 6)
	{
		tmpstr[24] = '1';
		tmpstr[46] = '1';
		return tmpstr;
	}
	// hengxian
	if (index1 == 0 && index2 == 2)
	{
		tmpstr[1] = '1';
		tmpstr[12] = '1';
		return tmpstr;
	}
	if (index1 == 3 && index2 == 5)
	{
		tmpstr[34] = '1';
		tmpstr[45] = '1';
		return tmpstr;
	}
	if (index1 == 6 && index2 == 8)
	{
		tmpstr[67] = '1';
		tmpstr[78] = '1';
		return tmpstr;
	}
	// shuxian
	if (index1 == 0 && index2 == 6)
	{
		tmpstr[3] = '1';
		tmpstr[36] = '1';
		return tmpstr;
	}
	if (index1 == 1 && index2 == 7)
	{
		tmpstr[14] = '1';
		tmpstr[47] = '1';
		return tmpstr;
	}
	if (index1 == 2 && index2 == 8)
	{
		tmpstr[25] = '1';
		tmpstr[58] = '1';
		return tmpstr;
	}
	tmpstr[index1 * 10 + index2] = '1';
	return tmpstr;
}

string combine(string & str, string tmp)
{
	string res;
	for (int i = 0; i < 100; i++)
	{
		if (tmp[i] == '1' || str[i] == '1')
			res.push_back('1');
		else
			res.push_back('0');
	}
	return res;
}

// @param path 划过的顶点 
// @param free 剩下可用的顶点
void dfs(vector & path, vector & free, string route)
{
	if (path.size() == points.size())
		return;
	for (int i = 0; i < points.size(); i++)
	{
		if (free[i])
		{
			string tttt = int2str(path.back(), points[i]);
			tttt = combine(route, tttt);
			allimg.insert(tttt);
			path.push_back(points[i]);
			free[i] = false;
			dfs(path, free, tttt);
			path.pop_back();
			free[i] = true;
		}
	}
}

int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		points.clear();
		allimg.clear();

		string str;
		cin >> str;
		if (str[0] == '.')
			points.push_back(0);
		if (str[1] == '.')
			points.push_back(1);
		if (str[2] == '.')
			points.push_back(2);
		cin >> str;
		if (str[0] == '.')
			points.push_back(3);
		if (str[1] == '.')
			points.push_back(4);
		if (str[2] == '.')
			points.push_back(5);
		cin >> str;
		if (str[0] == '.')
			points.push_back(6);
		if (str[1] == '.')
			points.push_back(7);
		if (str[2] == '.')
			points.push_back(8);

		vector path;
		vector free(points.size(), true);
		string tstr(100, '0');
		for (int i = 0; i < points.size(); i++)
		{
			path.push_back(points[i]);
			free[i] = false;
			dfs(path, free, tstr);
			free[i] = true;
			path.pop_back();
		}

		cout << allimg.size() << endl;
	}

	return 0;
}

测试样例:

前三个是官方给的样例

输入:

5
...
000
000
...
000
0.0
.0.
0.0
.0.
.0.
000
.0.
...
.0.
...

输出:

3
22
111
30
44934

 

 

 

 

 

 

 

你可能感兴趣的:(C++,C++应用笔记,算法)