洛谷 P2831 [NOIP2016]愤怒的小鸟

传送

一些废话

题太难了,于是这次不写解析,具体思路可以看这位聚聚的博客:

聚聚的博客

这里说对搜索题的一点看法。

首先是实数域上的搜索。对于本题而言,显然抛物线的每个参数都是实数,断绝了一般枚举的可能。对于这类情况,常用的转化是:

枚举所有的情况 -> 枚举可能的情况

对于本题,具体而言,显然绝大部分抛物线都是无用的,而两只猪的坐标可以确定一条过原点的抛物线(当然,别忘了排除不合法的情况),所以我们枚举所有这样的猪对即可。

与本题类似地,有 (uva303) ,如果有兴趣可以去看一眼。

其次,每条抛物线可能击杀多只猪,但每次都去判断是否击杀某只猪过于奢侈,于是我们对每条抛物线就算一次,把能击杀的猪用二进制数存起来。这也是一个常用的优化策略避免赘余运算

以上。

参考程序

#include<iostream>
#include<vector>
#include<cmath>
#include<cstring>
using namespace std;
int n,m,t,bin[20][20],mem[1<<18];
const double eps = 1e-7;
const int inf = 0x3f3f3f3f;
struct pig{
	double x,y;
}p[20];
inline double calca(pig i,pig j){
	return (j.x * i.y - i.x * j.y)
	 / (i.x * j.x * (i.x - j.x));
}
inline double calcb(pig i,pig j){
	return (i.x * i.x * j.y - j.x * j.x * i.y)
	 / (i.x * j.x * (i.x - j.x));
}
void calc(double a,double b){
	vector<int> kill;
	int state = 0;
	for(int i = 0;i < n;i++)
		if(fabs(a * p[i].x * p[i].x + b * p[i].x - p[i].y) < eps){
			state |= (1<<i);
			kill.push_back(i);
		}
	for(int i = 0;i < kill.size();i++)
		for(int j = i + 1;j < kill.size();j++)
			bin[kill[i]][kill[j]] = bin[kill[j]][kill[i]] = state;
}
int dfs(int state){
	if(mem[state] != -1) return mem[state];
	mem[state] = inf;
	for(int i = 0;i < n;i++){
		if(state & (1<<i)) continue;
		bool flag = 0;
		for(int j = 0;j < n;j++)
			if((state & (1<<j)) == 0 && p[i].x != p[j].x && calca(p[i],p[j]) < 0.0){
				flag = 1;
				if(bin[i][j] == -1) calc(calca(p[i],p[j]),calcb(p[i],p[j]));
				mem[state] = min(mem[state],dfs(state | bin[i][j]) + 1);
			}
		if(!flag) return mem[state] = dfs(state | (1<<i)) + 1;
	}
	return mem[state];
	
}
int main(){
	cin >> t;
	while(t--){
		cin >> n >> m;
		for(int i = 0;i < n;i++)
			cin >> p[i].x >> p[i].y;
		memset(bin,-1,sizeof(bin));
		memset(mem,-1,sizeof(mem));
		mem[(1<<n)-1] = 0;
		cout << dfs(0) << endl;
	}
	return 0;
} 
原文地址:https://www.cnblogs.com/mysterious-garden/p/9884994.html