题目链接
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<
这样单次复杂度\(O(2^nn^2)\),有点悬
状态的\(2^n\)不能消掉我们从转移入手
原来每次枚举所有没有被覆盖的点是\(n^2\)的,我们固定其中一个点为没有被覆盖的点中编号最小的,这样转移就是\(n\)的
为什么这样是对的?因为我们最终要求的是覆盖所有的点,当前状态不覆盖最小的点也会被下一个状态覆盖,所以可以直接钦定一个点
这题样例都卡精度,醉了
#include
#include
#include
#include
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] << '\n';
}
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;
}