當年的兩個壓軸題如今都隨便作了呢 =w=ios
原題:數組
n<=18,t<=5ide
我當時怎麼就不會 系列233spa
一看這數據範圍,欸呀code
壯鴨低劈blog
預處理出任意兩個豬可以肯定的拋物線方程,最多隻有153個,而後檢查他們可以掃到哪些豬,把這些豬壓二進制壓進整數ci
第i個二進制位爲1就表示第i只豬能被掃到string
注意還有一隻鳥打一隻豬的狀況,也要處理出來放到一塊兒it
最初想的是直接dfs枚舉每條邊是否要選,可是邊數不少,事情並不簡單io
但其實也不須要搜索,DP就行233
f[i][j]表示直到第i個拋物線,打掉豬的子集j須要的最小代價
實際上開f數組的時候i這一維徹底不用,由於二進制壓位操做中或操做的特性,也不須要像揹包那樣必須從大到小轉移
只需先枚舉每一條拋物線i,再枚舉每個子集j,f[j|b[i]]=min(f[j|b[i]],f[j]+1)便可
拋物線總數不超過200,2^n最多隻有262144
因此直接n^2*2^n dp就能夠了233
注意一些細節:
1.題目要求a<0,注意判斷
2.浮點數判斷是abs(a-b)<eps而不是a-b<eps,過久沒寫忘了233
3.注意一隻鳥只打一隻豬的狀況也要預處理,由於可能存在沒有一頭豬可以和別的豬湊成a<0的拋物線的狀況
我如今去打16NOIP豈不是就是600到手233
代碼:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 using namespace std; 7 #define LL long long 8 const double eps=1e-6; 9 const int oo=1000000007; 10 struct nds{double x,y;}a[20]; 11 int n; int N; 12 int b[200],btp=0; 13 int f[262144]; 14 void prvs(){ 15 btp=0; 16 } 17 int main(){ 18 //freopen("ddd.in","r",stdin); 19 int T; cin>>T; 20 while(T --> 0){ 21 int p; 22 scanf("%d%d",&n,&p); prvs(); 23 N=(1<<n)-1; 24 for(int i=1;i<=n;++i) scanf("%lf%lf",&a[i].x,&a[i].y); 25 for(int i=1;i<n;++i)for(int j=i+1;j<=n;++j)if(abs(a[i].x-a[j].x)>eps){ 26 double tma=(a[j].y-a[i].y*a[j].x/a[i].x)/(a[j].x*(a[j].x-a[i].x)); 27 double tmb=a[i].y/a[i].x-a[i].x*tma; 28 if(tma<0){ //注意要求 29 b[++btp]=0; 30 for(int k=1;k<=n;++k)if(abs(a[k].x*a[k].x*tma+a[k].x*tmb-a[k].y)<eps) 31 //注意abs 32 b[btp]|=(1<<(k-1)); 33 } 34 } 35 for(int i=1;i<=n;++i) b[++btp]=(1<<(i-1)); 36 //注意有可能不存在能夠配對的點 37 for(int i=1;i<=N;++i) f[i]=oo; 38 f[0]=0; 39 for(int i=1;i<=btp;++i)for(int j=0;j<=N;++j) 40 f[j|b[i]]=min(f[j|b[i]],f[j]+1); 41 printf("%d\n",f[N]); 42 } 43 return 0; 44 }