天天看點

[NOIP2016提高組]憤怒的小鳥

題目:UOJ#265、洛谷P2831、Vijos P2008。

題目大意:有n頭豬,都在一個二維坐标系裡(每頭豬坐标為兩位小數)。規定每隻鳥能從(0,0)處發射,且經過的抛物線一定為$y=ax^2+bx$,且$a<0$。

如果幾頭豬頭豬在同一條抛物線上,那麼它就能被一隻鳥打死。問至少發射多少隻鳥才能打死所有的豬?

解題思路:由于n最大才18,我們可以用二進制的每一個位來儲存一隻鳥,做一個狀壓DP。

那麼就是判斷抛物線的事了。我們枚舉兩頭豬,算出a和b的值。由于鳥從原點飛出,我們直接套公式計算即可。

計算形如

[NOIP2016提高組]憤怒的小鳥

的二進制一次方程,直接套公式

[NOIP2016提高組]憤怒的小鳥

即可。

然後判斷a是否小于0即可。如果是,則說明存在這條抛物線,那我們繼續枚舉所有豬i,看是否在這條抛物線上,如果是就把1<<(i-1)的值加到抛物線上。

注意如果兩頭豬一樣,那麼該抛物線就是這一頭豬。

最後類似背包的DP即可。

可以發現抛物線數量最壞是$n^2$級别的,是以時間複雜度$O(n^3+2^n n^2)$。

C++ Code:

#include<cstdio>
#include<cstring>
#include<cmath>
#define eps (1e-13)
using namespace std;
struct Vec{
	long double x,y;
}e[22];
int f[1<<22],g[666];
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		int n,m;
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;++i)
		scanf("%Lf%Lf",&e[i].x,&e[i].y);
		int cnt=0;
		for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j)
		if(i!=j){
			long double a=(e[j].x*e[i].y-e[i].x*e[j].y)/(e[i].x*e[i].x*e[j].x-e[j].x*e[j].x*e[i].x),
				   b=(e[i].x*e[i].x*e[j].y-e[j].x*e[j].x*e[i].y)/(e[i].x*e[i].x*e[j].x-e[i].x*e[j].x*e[j].x);
			if(-a>eps){
				int num=0;
				for(int k=1;k<=n;++k)
				if(fabs(a*e[k].x*e[k].x+b*e[k].x-e[k].y)<eps)
				num|=1<<(k-1);
				g[++cnt]=num;
			}	
		}else
		g[++cnt]=1<<(i-1);
		memset(f,0x3f,sizeof f);
		f[0]=0;
		for(int i=0;i<(1<<n);++i)
		for(int j=1;j<=cnt;++j)
		if(f[i|g[j]]>f[i]+1)f[i|g[j]]=f[i]+1;
		printf("%d\n",f[(1<<n)-1]);
	}
	return 0;
}
           

轉載于:https://www.cnblogs.com/Mrsrz/p/7598546.html