P2831 [NOIP2016 提高組] 憤怒的小鳥
阿新 • • 發佈:2021-08-10
狀壓dp+二次函式
,2式乘上\(x_1\),得
\[ax_1^2x_2 + bx_1x_2 = y_1x_2\\
ax_2^2x_1 + bx_1x_2 = y_2x_1
\]
Problem
平面上有\(n\)個在第一象限的點,求至少要用幾個類似於\(y = ax^2 + bx(a < 0,a,b \in \mathbb{R})\)的拋物線能將其全部覆蓋。
\(n \le 18\)
Solution
Thinking 1
\(1 \le n \le 18\),狀壓dp.
設\(dp[S]\)為S狀態下至少要用幾個拋物線。
Thinking 2
發現拋物線必經\((0,0)\)。所以再選兩點即可確定拋物線。
具體來說:現在有兩個點\((x_1,y_1),(x_2,y_2)\),那麼得:
1式乘上\(x_2\)
相減
\[a(x_1^2x_2 - x_2^2x_1) = y_1x_2 - y_2x_1\\ a = \dfrac{y_1x_2 - y_2x_1}{x_1^2x_2 - x_2^2x_1} \]\(b\)同理吧。
Thinking 3
考慮如何求\(dp[S]\):
- 若\(dp[S]\)的個數<2,則$dp[S] = $個數。
- otherwise,\(dp[S] = \min\{dp[S - s]\} + 1\)
其中\(s\)
在\(S\)中選兩點\((x_i,y_i),(x_j,y_j)\)
首先判斷其確定的表示式中的\(a\)是否小於0
然後求出\(k\),滿足\(i \neq j \neq k\)且\((x_k,y_k)\)在這個拋物線,\(s\)加上\(2^{k - 1}\)
注意s要加入i,j
做完了。
Thinking 4
其實我感覺用那個三點式和三分瞎搞搞也是能做的。
# include <bits/stdc++.h> using namespace std; # define int long long const int N = 22; const double eps = 1e-8; int T,n,m; struct node { double x,y; node() {} node(double _x,double _y) : x(_x),y(_y) {} }a[N]; int dp[(1 << 20) + 5]; double f(double x,double x1,double x2,double x3,double y1,double y2,double y3) { return (((x - x2) * (x - x3)) / ((x1 - x2) * (x1 - x3))) * y1 + (((x - x1) * (x - x3)) / ((x2 - x1) * (x2 - x3))) * y2 + (((x - x1) * (x - x2)) / ((x3 - x1) * (x3 - x2))) * y3; } void equ(double &a,double &b,double x1,double x2,double y1,double y2) { a = -((y1 * x2 - y2 * x1) / (x2 * x2 * x1 - x1 * x1 * x2)); b = ((y1 * x2 * x2) - (y2 * x1 * x1)) / (x1 * x2 * x2 - x1 * x1 * x2); return; } signed main(void) { // freopen("2831.in","r",stdin); // freopen("2831.out","w",stdout); scanf("%lld",&T); while(T--) { scanf("%lld%lld",&n,&m); for(int i = 1; i <= n; i++) { scanf("%lf%lf",&a[i].x,&a[i].y); } for(int i = 0; i < (1 << n); i++) { dp[i] = 0; for(int j = 1; j <= n; j++) { if(i >> (j - 1) & 1) { ++dp[i]; } } // printf("dp[%lld] = %lld\n",i,dp[i]); } for(int S = 0; S < (1 << n); S++) { int s = 0; for(int i = 1; i <= n; i++) { for(int j = i + 1; j <= n; j++) { if(!((S >> (i - 1) & 1) && (S >> (j - 1) & 1))) continue; if(fabs(a[i].x - a[j].x) < eps) continue; double A,B; equ(A,B,a[i].x,a[j].x,a[i].y,a[j].y); if(A > -eps) continue; s = (1 << (i - 1)) + (1 << (j - 1)); for(int k = 1; k <= n; k++) { if(k == i || k == j) continue; if(fabs(A * a[k].x * a[k].x + B * a[k].x - a[k].y) < eps) { // printf("i = %lld,j = %lld, k = %lld\n",i,j,k); s = (s + (1 << (k - 1))); } } if(S - s >= 0) dp[S] = min(dp[S],dp[S - s] + 1); } } } printf("%lld\n",dp[(1 << n) - 1]); } return 0; }