题意可以简单这样考虑
给出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; } }