题意:二维平面上有
n
n
n个点,现在需要用最少的形如
a
x
2
+
b
x
ax^2+bx
ax2+bx抛物线去完全覆盖这些点。
首先预处理出来如果攻击这两个点能消灭的所有点的集合。表示为
k
i
l
l
[
i
]
[
j
]
kill[i][j]
kill[i][j]
然后考虑
d
p
dp
dp。
设
d
p
[
i
]
dp[i]
dp[i]表示消灭
i
i
i这个集合的点需要的最少抛物线数。
转移:
- d p [ i ∣ k i l l [ x ] [ y ] ] = min ( d p [ i ∣ k i l l [ x ] [ y ] ] , d p [ i ] + 1 ) dp[i|kill[x][y]]=\min(dp[i|kill[x][y]],dp[i]+1) dp[i∣kill[x][y]]=min(dp[i∣kill[x][y]],dp[i]+1)
- d p [ i ∣ ( 1 < < ( j − 1 ) ] = min ( d p [ i ∣ ( 1 < < ( j − 1 ) ) ] , d p [ i ] + 1 ) dp[i|(1<<(j-1)]=\min(dp[i|(1<<(j-1))],dp[i]+1) dp[i∣(1<<(j−1)]=min(dp[i∣(1<<(j−1))],dp[i]+1)
考虑如何优化?
考虑无论怎么打,都需要打完所有的点,所以每次应当选择那个符合条件的最小的没打过的点,然后转移。
这部分也是可以预处理算出来的。
c
o
d
e
:
code:
code:
#include <bits/stdc++.h>
#define regi register int
int T;
int n;
int trans[2000000];
int Kill[20][20];
int f[2000000];
struct Pig{
double x;
double y;
}a[20];
main(){
scanf("%d",&T);
for(regi state=0,maxstate=1<<18;state<=maxstate;++state)
for(regi i=1;i<=18;++i)
if((state|(1<<i-1))>state){
trans[state]=i;
break;
}
while(T--){
scanf("%d%*d",&n);
for(regi i=1;i<=n;++i)
scanf("%lf%lf",&a[i].x,&a[i].y);
memset(Kill,0,sizeof Kill);
for(regi i=1;i<=n;++i)
for(regi j=1;j<=n;++j){
if(fabs(a[j].x-a[i].x)<1e-8)
continue;
double A=(a[j].y/a[j].x-a[i].y/a[i].x)/(a[j].x-a[i].x);
double B=(a[j].y-A*a[j].x*a[j].x)/a[j].x;
if(A>-1e-8)
continue;
Kill[i][j]=(1<<i-1)|(1<<j-1);
for(regi k=1;k<=n;++k){
if(fabs(a[k].y-A*a[k].x*a[k].x-B*a[k].x)<=1e-8)
Kill[i][j]|=(1<<k-1);
}
}
memset(f,0x3f,sizeof f);
f[0]=0;
for(regi state=0,maxstate=1<<n;state<maxstate;++state){
for(regi i=1;i<=n;++i)
f[state|(1<<i-1)]=std::min(f[state|(1<<i-1)],f[state]+1);
for(regi i=1;i<=n;++i)
f[state|Kill[trans[state]][i]]=std::min(f[state|Kill[trans[state]][i]],f[state]+1);
}
printf("%d\n",f[(1<<n)-1]);
}
return 0;
}
/*
预处理每个状态最小转移点,保证时间复杂度
然后预处理出打这两个点能打到的其它点
*/