之前做完飞扬的小鸟的时候还以为这道题和它一样。。
题意:用最少的鸟把猪打完。
题解:看n的范围可知是状压DP。枚举每两只猪之间的抛物线,若抛物线符合题意就看这条抛物线还覆盖了哪些猪。而有些猪可能不会和任意一只猪在同一抛物线上,所以还要新增一条抛物线只覆盖一只猪。
状态转移方程:
dp[i|para[j]]=min(dp[i|para[j]],dp[i]+1)
d
p
[
i
|
p
a
r
a
[
j
]
]
=
m
i
n
(
d
p
[
i
|
p
a
r
a
[
j
]
]
,
d
p
[
i
]
+
1
)
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const double eps = 1e-8;
const int MAXN = 21;
struct Node{
double x, y;
}P[MAXN];
int para[MAXN * MAXN], dp[(1 << 18) + 5];
inline void Solve(double x1, double y1, double x2, double y2, double &a, double &b){
a = (y2 * x1 - y1 * x2) / (x1 * x2 * x2 - x1 * x1 * x2);
b = (y2 * x1 * x1 - y1 * x2 * x2) / (x1 * x1 * x2 - x1 * x2 * x2);
}
inline bool check(double a, double b, double x, double y){ //检查第li条抛物线与第pi只猪是否相连
return fabs(a * x * x + b * x - y) < eps;
}
int main(){
freopen("in.txt", "r", stdin);
int T; scanf("%d", &T);
while(T--){
int n, m, cnt = 0; scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++)
scanf("%lf%lf", &P[i].x, &P[i].y);
for(int i = 1; i <= n; i++){
para[++cnt] = (1 << (i - 1)); //这条抛物线只经过一只小猪
for(int j = 1, vis = 0; j < i; j++){
if(fabs(P[i].x - P[j].x) < eps) continue;
if(vis & (1 << (j - 1))) continue; //之前已经有抛物线经过这只猪了
double a, b;
Solve(P[i].x, P[i].y, P[j].x, P[j].y, a, b);
if(a >= 0) continue; //不合法
para[++cnt] = (1 << (i - 1)); //这条抛物线起初只经过一只小猪
for(int k = 1; k < i; k++){
if(check(a, b, P[k].x, P[k].y)){
vis |= (1 << (k - 1));
para[cnt] |= (1 << (k - 1));
}
}
}
}
memset(dp, 0x3f, sizeof(dp)); dp[0] = 0;
for(int i = 0; i < (1 << n); i++){
for(int j = 1; j <= cnt; j++){
dp[i | para[j]] = min(dp[i | para[j]], dp[i] + 1);
}
}
printf("%d\n", dp[(1 << n) - 1]);
}
return 0;
}
循环的时候把起点设为0会更好写,但我不想改了233