1. 程式人生 > >NOIP2016 憤怒的小鳥 狀壓DP

NOIP2016 憤怒的小鳥 狀壓DP

之前做完飛揚的小鳥的時候還以為這道題和它一樣。。

題意:用最少的鳥把豬打完。
題解:看n的範圍可知是狀壓DP。列舉每兩隻豬之間的拋物線,若拋物線符合題意就看這條拋物線還覆蓋了哪些豬。而有些豬可能不會和任意一隻豬在同一拋物線上,所以還要新增一條拋物線只覆蓋一隻豬。
狀態轉移方程:dp[i|para[j]]=min(dp[i|para[j]],dp[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