bzoj 5299 [Cqoi2018]解锁屏幕 状压dp

题面

题目传送门

解法

非常基础的状压dp

  • 这个状压dp我感觉可以出成提高组难度啊
  • f [ i ] [ j ] 表示当前最后一个被划到的点为 i ,所有点被划到的情况为 j 0 表示还没有被划到, 1 表示已经被划到了
  • 考虑如何转移,枚举上一次划到的点 k ,如果 i k 的连线中所有点全部被划到了,那么 f [ i ] [ j ] + = f [ k ] [ j ] ,点对两两连线中的点可以通过 O ( n 3 ) 的时间预处理出来
  • 最后加上所有不小于 4 个点的就行了
  • 时间复杂度: O ( 2 n × n 2 )

代码

#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;
}

猜你喜欢

转载自blog.csdn.net/emmmmmmmmm/article/details/82012742