题目描述
给定(N)个点,和形式抛物线(y = ax^2 + bx),每次可以消去抛物线上的点,问消去所有点的最少抛物线数时是多少?
范围
(N leq 18)
题解
题目相当于dancing links
思想,用(n^2)个抛物线去覆盖点集,那么设(f_i)表示覆盖了第(i)列的最少行数是多少,转移:
(f_{i|j} = min(f_i + 1,f_{i|j}))
预处理经过任意两点的抛物线所覆盖集合即可。
复杂度(O(T(n^3 + n2^n)))
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 18;
const double eps = 1e-8;
struct node {
double x,y;
}pos[N];
int cmp(double x,double y) {
if(fabs(x - y) < eps) return 0;
if(x < y) return -1;
return 1;
}
int n,m;
int path[N][N];
int f[1 << N];
int main () {
int T;
cin >> T;
while(T --) {
cin >> n >> m;
for(int i = 0;i < n; ++i) {
cin >> pos[i].x >> pos[i].y;
}
memset(path,0,sizeof path);
for(int i = 0; i < n; ++i) {
path[i][i] = 1 << i;
for(int j = 0;j < n; ++j) {
double x1 = pos[i].x;
double x2 = pos[j].x;
double y1 = pos[i].y;
double y2 = pos[j].y;
if(!cmp(x1,x2)) continue;
double a = (y1 / x1 - y2 / x2) / (x1 - x2);
double b = y1 / x1 - a * x1;
if(cmp(a,0) >= 0) continue;
int sta = 0;
for(int k = 0;k < n; ++k) {
double xx = pos[k].x;
double yy = pos[k].y;
if(!cmp(a * xx * xx + b * xx,yy)) {
sta += 1 << k;
}
}
path[i][j] = sta;
}
}
memset(f,0x3f,sizeof f);
f[0] = 0;
for (int i = 0;i < (1 << n) - 1; ++i) {
int tmp = 0;
for(int j = 0;j < n; ++j) {
if(!(i >> j & 1)) {
tmp = j;
break;
}
}
for(int j = 0;j < n; ++j) {
f[i | path[tmp][j]] = min(f[i] + 1,f[i | path[tmp][j]]);
}
}
cout << f[(1 << n) - 1] << endl;
}
return 0;
}