【noip2016d2t3】状压DP+巧妙优化

题意可以简单这样考虑

给出n^2个集合(每个集合的元素不超过n),包含某个元素的集合至少有n个,选出最少的集合,使这些集合的并包含n个元素

n最大只有18

可以考虑状压n个元素,然后枚举n^2个集合

这样的复杂度是2^n*n^2

但是我们可以这样考虑,因为最终的集合一定包含所有的元素,所以不妨预处理出每个状态中最靠左的0的位置,

然后每次只需要考虑包含这个元素的n个集合(因为你最终一定要包含这个元素,所以不如就先包含它)

这样复杂度就降低成2^n*n

注意精度,以及两个点不一定能构成抛物线

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

struct Pair
{
    double x, y;
    Pair() {}
    Pair(double _x, double _y):x(_x), y(_y) {}
}p[20];
int Map[400][20];
vector<int> State;
int f[(1<<(20))], Hash[(1<<(20))];
Pair Get(Pair A, Pair B)
{
    if(A.x == B.x && A.y == B.y) B.x = A.x+1;
    double y = (B.y*A.x - B.x*A.y)/(B.x*B.x*A.x - A.x*A.x*B.x);
    double x = (A.y - y*A.x*A.x)/A.x;
    return Pair(x, y);
}
const double eps = 1e-3;
bool On(Pair A, Pair X)
{
    return abs(X.y - A.x*X.x - A.y*X.x*X.x) < eps;
}
int n, m, T;
int And(int X, int i)
{
    for(int j = 1; j <= n; j++)
        if(Map[i][j]) X = X|(1<<(j-1));
    return X;
}
void debug(int X)
{
    for(int i = 0; i < n; i++) if(X&(1<<i)) cout<<1; else cout<<0;
    cout<<endl;
}
int main()
{
    for(int i = 0; i <= (1<<20)-1; i++)
    {
        for(int j = 0; j <= 20; j++)
            if((i&(1<<j)) == 0) { Hash[i] = j+1; break; }
    }
    cin>>T;
    while(T--)
    {
        cin>>n>>m;
        memset(Map, 0, sizeof(Map));
        memset(f, 0, sizeof(f));
        State.clear(); State.push_back(0);
        for(int i = 1; i <= n; i++) cin>>p[i].x>>p[i].y;
        int tot = 1;
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
            {
                Pair temp = Get(p[i], p[j]);
                if(temp.y >= 0) continue;
                for(int k = 1; k <= n; k++)
                    if(On(temp, p[k]))
                        Map[(i-1)*n+j][k] = 1;
            }
        int k = 0, M = (1<<n)-1;
        while(f[M] == 0)
        {
            int sz = State.size();
            for(int s = k; s < sz; s++)
            {
                for(int i = 1; i <= n; i++)
                {
                    int s2 = And(State[s], (Hash[State[s]]-1)*n+i);                  
                    if(f[s2] == 0 && s2 != 0)
                    {
                        f[s2] = f[State[s]] + 1;
                        State.push_back(s2);
                    }else
                        f[s2] = min(f[s2], f[State[s]] + 1);
                }
            }
            k = sz;          
        }
        cout<<f[(1<<n)-1]<<endl;
    }
}
原文地址:https://www.cnblogs.com/Saurus/p/6086543.html