luogu P2831 [NOIP2016 提高組] 憤怒的小鳥
阿新 • • 發佈:2021-01-01
題面傳送門
資料這麼小顯然狀壓dp
考慮狀壓\(dp_i\)表示打掉的集合為\(i\)時最少拋物線條數。
預處理\(f_{i,j}\)表示過\(i\)點和\(j\)點的拋物線經過的點的集合。
但是這樣的轉移是\(O(T2^nn^2)\)的。
考慮怎麼優化。
很明顯我們的\(dp\)轉移是無序的。將其變成有序即先打最小的點則可優化掉一個\(n\)變為\(O(T2^nn)\)
程式碼實現:
#include<cstdio> #include<cstring> #define abs(x) ((x)>0?(x):-(x)) #define min(a,b) ((a)<(b)?(a):(b)) #define eps 1e-6 using namespace std; int n,m,dp[1000039],g[1000039],t,a,b,f[39][39]; double x[39],y[39],nowx,nowy,x1,x2,y1,y2; int main(){ // freopen("1.in","r",stdin); register int i,j,k; scanf("%d",&t); for(i=0;i<(1<<18);i++){ for(j=1;j<=18;j++) if(!(i&(1<<j-1))){g[i]=j;break;} } while(t--){ scanf("%d%d",&n,&m);memset(dp,0x3f,sizeof(dp));dp[0]=0; for(i=1;i<=n;i++) scanf("%lf%lf",&x[i],&y[i]),dp[(1<<i-1)]=1; for(i=1;i<=n;i++){ for(j=1;j<=n;j++){ f[i][j]=0; x1=x[i];x2=x[j];y1=y[i];y2=y[j]; if(abs(x1-x2)<=eps) continue; nowx=(y1*x2-y2*x1)/(x1*x1*x2-x2*x2*x1); nowy=(y1-nowx*x1*x1)/x1; if(nowx>0) continue; for(k=1;k<=n;k++) if(abs(nowx*x[k]*x[k]+nowy*x[k]-y[k])<=eps) f[i][j]|=1<<k-1; } } for(i=0;i<(1<<n)-1;i++){ for(j=1;j<=n;j++) dp[i|f[g[i]][j]]=min(dp[i|f[g[i]][j]],dp[i]+1); for(j=1;j<=n;j++) dp[i|(1<<j-1)]=min(dp[i|(1<<j-1)],dp[i]+1); } printf("%d\n",dp[(1<<n)-1]); } }