1. 程式人生 > >P2831 憤怒的小鳥

P2831 憤怒的小鳥

algorithm www. long blank -- set == org tps

傳送門

看到數據範圍就知道是搜索或狀壓DP

算了一波復雜度搜索好像過不了極限數據

搞狀壓

設 f [ i ] 表示所有豬的狀態為 i (二進制下1表示死了,0表示沒死)時需要的最少發射次數

設 p [ i ] [ j ] 存經過第 i 只豬和第 j 只豬的拋物線經過的豬的狀態(可以$n^2$預處理出來,解方程都會吧..)

找到第一個沒死的豬 i ,然後枚舉所有其他沒死的豬 j ,進行轉移:

f [ i|p [ i ] [ j ] ] = min ( f [ i|p [ i ] [ j ] ],f [ i ] + 1 )

不用 n^2 枚舉所有兩只豬的 p [ i ] [ j ] ,因為第一頭豬現在不死以後也要死,所以沒有任何區別

當然還要考慮只經過一頭豬的情況: f [ i|(1<<i-1) ] = min ( f[ i|(1<<i-1) ],f [ i ] +1 )

註意拋物線解析式中 a<0,記得判一下合法性

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
inline int read()
{
    
int x=0,f=1; char ch=getchar(); while(ch<0||ch>9) { if(ch==-) f=-1; ch=getchar(); } while(ch>=0&&ch<=9) { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const double eps=1e-8; const int N=300007; int n,m,T; struct data { double x,y; }d[
27]; double a,b; inline void slove(double x1,double y1,double x2,double y2)//解方程 { double t=x2*x2/x1/x1; b=(y2-y1*t)/(x2-x1*t); a=(y1-x1*b)/x1/x1; } int p[27][27]; int f[N]; void pre() { memset(p,0,sizeof(p)); memset(f,0x3f,sizeof(f)); f[0]=0;//記得初始化 for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) { if(fabs(d[i].x-d[j].x)<eps) continue;//如果橫坐標相同則無解 slove(d[i].x,d[i].y,d[j].x,d[j].y); if(a>=0) continue;//判斷合法性 for(int k=1;k<=n;k++) if(fabs(a*d[k].x*d[k].x+b*d[k].x-d[k].y)<=eps)/*如果拋物線經過豬k就記錄一下*/ p[i][j]|=(1<<k-1); } } int main() { T=read(); while(T--) { n=read(); m=read(); for(int i=1;i<=n;i++) scanf("%lf%lf",&d[i].x,&d[i].y); pre(); int mx=(1<<n)-1,pos; for(int i=0;i<=mx;i++) { for(int j=0;j<n;j++) if( !((i>>j)&1) ) { pos=j; break; }//找到第一只沒死的豬 f[i|(1<<pos)]=min(f[i|(1<<pos)],f[i]+1);//單獨考慮 for(int j=pos+1;j<n;j++)//與其他豬一起考慮 if( !((i>>j)&1) ) f[i|p[pos+1][j+1]]=min(f[i|p[pos+1][j+1]],f[i]+1);//註意p的下標 } printf("%d\n",f[mx]); } return 0; }

P2831 憤怒的小鳥