NOIP2016 憤怒的小鳥 狀壓DP
阿新 • • 發佈:2019-01-29
之前做完飛揚的小鳥的時候還以為這道題和它一樣。。
題意:用最少的鳥把豬打完。
題解:看n的範圍可知是狀壓DP。列舉每兩隻豬之間的拋物線,若拋物線符合題意就看這條拋物線還覆蓋了哪些豬。而有些豬可能不會和任意一隻豬在同一拋物線上,所以還要新增一條拋物線只覆蓋一隻豬。
狀態轉移方程:
#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