Description
使用过 Android 手机的同学一定对手势解锁屏幕不陌生。 Android 的解锁屏幕由
3×3
3
×
3
个点组成,手指在屏幕上画一条
线将其中一些点连接起来,即可构成一个解锁图案。如下面三个例子所示:
画线时还需要遵循一些规则
1.连接的点数不能少于
4
4
个。也就是说只连接两个点或者三个点会提示错误。
2.两个点之间的连线不能弯曲。
3.每个点只能“使用”一次,不可重复。这里的“使用”是指手指划过一个点,该点变绿。
4.两个点之间的连线不能“跨过”另一个点,除非那个点之前已经被“使用”过了。
对于最后一条规则,参见下图的解释。左边两幅图违反了该规则:而右边两幅图
(分别为 和
5→4→1→9→2
5
→
4
→
1
→
9
→
2
)
则没有违反规则,因为在“跨过”点时,点已经被“使用”过了。
现在工程师希望改进解锁屏幕,增减点的数目,并移动点的位置,不再是一个九宫格形状,但保持上述画线的规则不变。
请计算新的解锁屏幕上,一共有多少满足规则的画线方案。
Input
输入文件第一行,为一个整数
n
n
,表示点的数目。
接下来 行,每行两个空格分开的整数
xi
x
i
和
yi
y
i
,表示每个点的坐标。
−1000≤xi,yi≤1000
−
1000
≤
x
i
,
y
i
≤
1000
,
1≤n<20
1
≤
n
<
20
。各点坐标不相同。
Output
输出文件共一行,为题目所求方案数除以 100000007 100000007 的余数。
Sample Input
4
0 0
1 1
2 2
3 3
Sample Output
8
样例解释
设
4
4
个点编号为 到
4
4
,方案有
及其镜像
4→3→2→1,3→4→2→1,2→3→4→1,3→2→4→1
4
→
3
→
2
→
1
,
3
→
4
→
2
→
1
,
2
→
3
→
4
→
1
,
3
→
2
→
4
→
1
Solution
先预处理出对于每两个点
(i,j)
(
i
,
j
)
,连成的边会经过哪些点(不包括
i
i
和 ),压缩成一个二进制数表示集合,记为
set[i,j]
s
e
t
[
i
,
j
]
。
然后显然状压dp:
f[S][i]
f
[
S
]
[
i
]
表示经过的点集为
S
S
,最后一个到达的点为 的方案数。
边界:
|S|=1
|
S
|
=
1
时,如果
S={i}
S
=
{
i
}
,那么
f[S][i]=1
f
[
S
]
[
i
]
=
1
,否则
f[S][i]=0
f
[
S
]
[
i
]
=
0
。
转移:枚举
j∈S
j
∈
S
且
j≠i
j
≠
i
且
set[i,j]⊂S
s
e
t
[
i
,
j
]
⊂
S
:
最后答案:
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
const int N = 23, C = (1 << 20) + 5, PYZ = 1e8 + 7;
int n, Cm, X[N], Y[N], to[N][N], f[C][N], cnt[C], ans;
bool check(int x1, int y1, int x2, int y2, int x3, int y3) {
int x4 = x1 - x2, y4 = y1 - y2, x5 = x3 - x2, y5 = y3 - y2;
if (x1 > x3) swap(x1, x3); if (y1 > y3) swap(y1, y3);
return x4 * y5 == x5 * y4 && x1 <= x2 && x2 <= x3 && y1 <= y2 && y2 <= y3;
}
int main() {
int i, j, k; n = read(); For (i, 1, n) X[i] = read(), Y[i] = read();
For (i, 1, n) For (j, 1, n) if (i != j) For (k, 1, n)
if (k != i && k != j && check(X[i], Y[i], X[k], Y[k], X[j], Y[j]))
to[i][j] |= 1 << k - 1; Cm = (1 << n) - 1;
For (i, 1, Cm) {
For (j, 1, n) if ((i >> j - 1) & 1) cnt[i]++; For (j, 1, n) {
if (!((i >> j - 1) & 1)) continue;
if (cnt[i] == 1) {f[i][j] = 1; continue;}
For (k, 1, n) if (((i >> k - 1) & 1) && j != k
&& (to[j][k] & i) == to[j][k])
f[i][j] = (f[i][j] + f[i ^ (1 << j - 1)][k]) % PYZ;
if (cnt[i] > 3) ans = (ans + f[i][j]) % PYZ;
}
}
cout << ans << endl; return 0;
}