题面
解法
非常基础的状压dp
这个状压dp我感觉可以出成提高组难度啊- 设 表示当前最后一个被划到的点为 ,所有点被划到的情况为 。 表示还没有被划到, 表示已经被划到了
- 考虑如何转移,枚举上一次划到的点 ,如果 和 的连线中所有点全部被划到了,那么 ,点对两两连线中的点可以通过 的时间预处理出来
- 最后加上所有不小于 个点的就行了
- 时间复杂度:
代码
#include <bits/stdc++.h>
#define Mod 100000007
#define N 21
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
x = 0; int f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Node {
int x, y;
} a[N];
int lg[1 << N], l[N][N], f[N][1 << N];
int lowbit(int x) {return x & -x;}
int calc(int x) {
int ret = 0;
while (x) x -= lowbit(x), ret++;
return ret;
}
bool check(int k, int i, int j) {
if (min(a[i].x, a[j].x) > a[k].x || a[k].x > max(a[i].x, a[j].x)) return false;
if (min(a[i].y, a[j].y) > a[k].y || a[k].y > max(a[i].y, a[j].y)) return false;
return (a[k].y - a[i].y) * (a[j].x - a[k].x) == (a[k].x - a[i].x) * (a[j].y - a[k].y);
}
int main() {
int n; read(n); lg[1] = 1;
for (int i = 2; i < (1 << n); i++) lg[i] = lg[i / 2] + 1;
for (int i = 1; i <= n; i++)
read(a[i].x), read(a[i].y);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
if (i == j) continue;
for (int k = 1; k <= n; k++) {
if (k == i || k == j) continue;
if (check(k, i, j)) l[i][j] |= 1 << k - 1;
}
}
for (int i = 1; i <= n; i++) f[i][1 << i - 1] = 1;
for (int j = 1; j < (1 << n); j++) {
if (lowbit(j) == j) continue;
for (int i = 1; i <= n; i++) {
if (((j >> i - 1) & 1) == 0) continue;
for (int tx = j, ty = lowbit(tx); tx; tx -= lowbit(tx), ty = lowbit(tx)) {
int k = lg[ty];
if ((l[k][i] & j) == l[k][i]) f[i][j] = (f[i][j] + f[k][j ^ (1 << i - 1)]) % Mod;
}
}
}
int ans = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j < (1 << n); j++)
if (calc(j) >= 4) ans = (ans + f[i][j]) % Mod;
cout << ans << "\n";
return 0;
}