P2831 憤怒的小鳥
阿新 • • 發佈:2018-10-21
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 憤怒的小鳥