Luogu
算法
思路
由于 n≤18,可以考虑搜索。
定义 dfs(cur,a,b) 表示搜索到了第 cur 头猪,使用了 a 条抛物线,前面还有 b 头猪为被击中。
对于一条抛物线 y=ax2+bx+c,需要三个点确定其解析式。由于题目钦定了抛物线必定经过 (0,0),所以还需要两个点。在前面为被击中的 b 头猪中选择一头与第 cur 头猪配对。假设两头猪的坐标为 (x1,y1),(x2,y2)。则
{ax12+bx1=y1ax22+bx2=y2→{a=x12x2−x1x22(y1x2−y2x1)b=x1y1−x12a
于是就可以得到这两头猪配对的抛物线解析式。
然后进行剪枝。如果由于前面未配对的猪必须要有一条单独的抛物线经过,所以有最优性剪枝: a+b>answer 时 return。
代码
#include <bits/stdc++.h>
using namespace std;
constexpr int N = 25; constexpr double EPS = 1e-9; pair<double, double> pig[N], line[N], unused[N]; int test_case, n, m, answer;
bool equal(double a, double b) { return fabs(a - b) < EPS; }
void dfs(int cur, int a, int b) { if (a + b >= answer) { return void(); } else if (cur > n) { answer = min(answer, a + b); return void(); } double x = pig[cur].first, y = pig[cur].second; bool crossed = false; for (int i = 1; i <= a; i++) { if (equal(line[i].first * x * x + line[i].second * x, y) == true) { crossed = true; break; } } if (crossed == true) { dfs(cur + 1, a, b); } else { for (int i = 1; i <= b; i++) { double tx = unused[i].first, ty = unused[i].second; if (equal(x, tx) == true) { continue; } double pa = (y * tx - ty * x) / (x * x * tx - tx * tx * x); double pb = (y - x * x * pa) / x; if (pa >= 0) { continue; } line[a + 1] = make_pair(pa, pb); pair<double, double> tmp = unused[i]; for (int j = i; j <= b - 1; j++) { unused[j] = unused[j + 1]; } dfs(cur + 1, a + 1, b - 1); for (int j = b; j > i; j--) { unused[j] = unused[j - 1]; } unused[i] = tmp; } unused[b + 1] = pig[cur]; dfs(cur + 1, a, b + 1); } }
int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); cin >> test_case; while (test_case --) { cin >> n >> m; for (int i = 1; i <= n; i++) { cin >> pig[i].first >> pig[i].second; } answer = 100000; dfs(1, 0, 0); cout << answer << "\n"; } return 0; }
|