[NOIP2016提高組]憤怒的小鳥
阿新 • • 發佈:2017-09-26
noi 二維 枚舉 def 拋物線 ++ scanf pre sizeof
題目:UOJ#265、洛谷P2831、Vijos P2008。
題目大意:有n頭豬,都在一個二維坐標系裏(每頭豬坐標為兩位小數)。規定每只鳥能從(0,0)處發射,且經過的拋物線一定為$y=ax^2+bx$,且$a<0$。
如果幾頭豬頭豬在同一條拋物線上,那麽它就能被一只鳥打死。問至少發射多少只鳥才能打死所有的豬?
解題思路:由於n最大才18,我們可以用二進制的每一個位來保存一只鳥,做一個狀壓DP。
那麽就是判斷拋物線的事了。我們枚舉兩頭豬,算出a和b的值。由於鳥從原點飛出,我們直接套公式計算即可。
計算形如的二元一次方程,直接套公式即可。
然後判斷a是否小於0即可。如果是,則說明存在這條拋物線,那我們繼續枚舉所有豬i,看是否在這條拋物線上,如果是就把1<<(i-1)的值加到拋物線上。
註意如果兩頭豬一樣,那麽該拋物線就是這一頭豬。
最後類似背包的DP即可。
可以發現拋物線數量最壞是$n^2$級別的,所以時間復雜度$O(n^3+2^n n^2)$。
C++ Code:
#include<cstdio> #include<cstring> #include<cmath> #define eps (1e-13) using namespace std; struct Vec{ long double x,y; }e[22]; int f[1<<22],g[666]; int main(){ int t; scanf("%d",&t); while(t--){ int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) scanf("%Lf%Lf",&e[i].x,&e[i].y); int cnt=0; for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) if(i!=j){ long double a=(e[j].x*e[i].y-e[i].x*e[j].y)/(e[i].x*e[i].x*e[j].x-e[j].x*e[j].x*e[i].x), b=(e[i].x*e[i].x*e[j].y-e[j].x*e[j].x*e[i].y)/(e[i].x*e[i].x*e[j].x-e[i].x*e[j].x*e[j].x); if(-a>eps){ int num=0; for(int k=1;k<=n;++k) if(fabs(a*e[k].x*e[k].x+b*e[k].x-e[k].y)<eps) num|=1<<(k-1); g[++cnt]=num; } }else g[++cnt]=1<<(i-1); memset(f,0x3f,sizeof f); f[0]=0; for(int i=0;i<(1<<n);++i) for(int j=1;j<=cnt;++j) if(f[i|g[j]]>f[i]+1)f[i|g[j]]=f[i]+1; printf("%d\n",f[(1<<n)-1]); } return 0; }
[NOIP2016提高組]憤怒的小鳥