P2831 [NOIP2016 提高组] 愤怒的小鸟
抛物线从 $(0, 0)$ 开始,而三点确定一条抛物线,我们还需要两个点,也就是说,一条抛物线至少可以击中两只小猪(不考虑重复),直接枚举,将可能的抛物线记录下来。再记录这条抛物线可以击中哪些猪。$n$ 很小,考虑状态压缩,$f(x)$ 表示击中状态 $x$ 的小猪至少需要的小鸟的个数,记忆化搜索即可。注意考虑精度问题。
查看代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| #include <iostream> #include <cstdio> #include <cmath> #include <cstring> using namespace std; typedef pair <double, double> PDD; const int N = 20; const double eps = 1e-6; int n, m, idx, f[1 << 20]; PDD pig[N]; struct Bird { double a, b; int avai; }birds[210]; int dfs (int x) { if (~f[x]) return f[x]; int res = n; for (int i = 1; i <= idx; i++) if ((x | birds[i].avai) != x) res = min(res, dfs(x | birds[i].avai) + 1); f[x] = res; return res; } int main () { int T; cin >> T; while (T--) { memset(birds, 0, sizeof birds); cin >> n >> m; idx = 0; for (int i = 0; i < 1 << n; i++) f[i] = -1; f[(1 << n) - 1] = 0; for (int i = 0; i < n; i++) cin >> pig[i].first >> pig[i].second; for (int i = 0; i < n; i++) for (int j = i + 1; j < n; j++) { double a = (pig[i].first * pig[j].first * (pig[j].first - pig[i].first)); if (abs(a) < eps) continue; a = (pig[i].first * pig[j].second - pig[i].second * pig[j].first) / a; if (a > -eps) continue; birds[++idx].a = a; birds[idx].b = (pig[i].second * pig[j].first * pig[j].first - pig[j].second * pig[i].first * pig[i].first) / (pig[i].first * pig[j].first * (pig[j].first - pig[i].first)); birds[idx].avai |= (1 << i) + (1 << j); } for (int i = 0; i < n; i++) for (int j = 1; j <= idx; j++) if (abs(birds[j].a * pig[i].first * pig[i].first + birds[j].b * pig[i].first - pig[i].second) < eps) birds[j].avai |= 1 << i; for (int i = 0; i < n; i++) birds[++idx].avai = (1 << i); cout << dfs(0) << endl; } return 0; }
|