【NOIP2016】憤怒的小鳥

當年的兩個壓軸題如今都隨便作了呢 =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 }
View Code
相關文章
相關標籤/搜索