Solution 愤怒的小鸟
题目大意:第一象限内有(n)个点((n leq 18)),求最少要多少条形如(y=ax^2+bx quad a<0,a,b, in R) 的抛物线才能覆盖所有点
状压(dp)
分析:(n)的数据范围很小,因此我们可以考虑状压(dp),填表法不好做我们可以用刷表法
用(f[S])表示覆盖集合(S)内点的最小代价
显然(f[0]= 0)
(f[S|line[i][j]] = min{f[S] + 1})其中(line[i][j])表示经过(i,j)两点的抛物线可以覆盖的点的集合
(f[S | 1<<i] = min{f[S] + 1})
这样单次复杂度(O(2^nn^2)),有点悬
状态的(2^n)不能消掉我们从转移入手
原来每次枚举所有没有被覆盖的点是(n^2)的,我们固定其中一个点为没有被覆盖的点中编号最小的,这样转移就是(n)的
为什么这样是对的?因为我们最终要求的是覆盖所有的点,当前状态不覆盖最小的点也会被下一个状态覆盖,所以可以直接钦定一个点
这题样例都卡精度,醉了
#include <algorithm>
#include <cmath>
#include <iostream>
#include <cstring>
using namespace std;
const int maxn = 23;
typedef long double type;
const type eps = 1e-7;
struct Pos{
type x,y;
}val[maxn];
int t,n,m,dp[1 << maxn],lowbit[1 << maxn],line[maxn][maxn];//lowbit表示最小的为0的点的编号
inline bool cmp(type a,type b){return abs(a - b) < eps;}
void gauss(type &x,type &y,type a1,type b1,type c1,type a2,type b2,type c2){
x = (c1 * b2 - c2 * b1) / (a1 * b2 - a2 * b1);
y = (c1 - a1 * x) / b1;
}
inline void solve(){
cin >> n >> m;
for(int i = 0;i < n;i++)
cin >> val[i].x >> val[i].y;
memset(line,0,sizeof(line));
memset(dp,0x3f,sizeof(dp));
dp[0] = 0;
for(int i = 0;i < n;i++)
for(int j = 0;j < n;j++){
if(cmp(val[i].x,val[j].x))continue;
type a,b;
gauss(a,b,val[i].x * val[i].x,val[i].x,val[i].y,val[j].x * val[j].x,val[j].x,val[j].y);
if(a > -eps)continue;
for(int k = 0;k < n;k++)
if(cmp(a * val[k].x * val[k].x + b * val[k].x,val[k].y))line[i][j] |= 1 << k;
}
for(int i = 0;i < 1 << n;i++){
int j = lowbit[i];
dp[i | (1 << j)] = min(dp[i | (1 << j)],dp[i] + 1);
for(int k = 0;k < n;k++)
dp[i | line[j][k]] = min(dp[i | line[j][k]],dp[i] + 1);
}
cout << dp[(1 << n) - 1] << '
';
}
int main(){
for(int i = 0;i < (1 << 18);i++){
int j = 0;
for(;(i >> j) & 1;j++);
lowbit[i] = j;
}
cin >> t;
while(t--)solve();
return 0;
}