洛谷P2831 愤怒的小鸟

题目

状压DP。

直接枚举二进制数表示当前猪有没有被消灭的状态。

最终答案的几条抛物线必定至少撞到一个猪。而且两头猪确定一条抛物线,可以枚举两头猪,分别求出他们的抛物线所消灭猪的状态,然后可以用类似背包的方法转移DP。

有方程:
\(dp[i|(\)当前抛物线的状态\()] = min(dp[i|(\)当前抛物线的状态\()],dp[i]+1)\)

有转移:

枚举每个状态,每个状态再枚举每个可以撞到的一头猪,再用该猪枚举是否存在既能撞到该猪,又能撞到另一头猪的抛物线,然后即可确定抛物线,也可确定抛物线的状态了。根据抛物线的状态,即可实现转移

#include <bits/stdc++.h>
#define N 1001001
using namespace std;
int T, n, m;
double x[1001], y[1001];
int vis[1001000], dp[1010011], ha[1001][1001];
double F(double a, double b, double x)
{
 	double y = a * x * x + b * x;
 	return y;
}
inline void add(int i, int j)//确定好所有抛物线的状态 
{
	if (abs(x[i] - x[j]) <= 0.0000001) return;//判断是否两个点重合 
	double b = (x[i] * x[i] * y[j] - x[j] * x[j] * y[i]) / (x[i] * x[i] * x[j] - x[j] * x[j] * x[i]);
	double a = (y[i] - b * x[i]) / (x[i] * x[i]);//两点确定一条抛物线。
	if (a < 0)//a必须小于0
	{
			ha[i][j] = (1 << i - 1) | (1 << j - 1); 
			for (int k = 1; k <= n; k++)
			if (abs(F(a, b, x[k]) - y[k]) <= 0.0000001 && k != i && k != j)
				ha[i][j] |= (1 << k - 1);//表示状态+=(1<<k-1) 用二进制表示状态。 
	}
		
}
inline void clear()
{
	memset(dp, 0x3f, sizeof(dp));
	memset(vis, 0, sizeof(vis));
	memset(ha, 0, sizeof(ha));
	dp[0] = 0;
}
int main()
{
 	scanf("%d", &T);
 	while (T--)
 	{
	 	clear();
 		scanf("%d%d", &n, &m);
 		for (int i = 1; i <= n; i++)
 			scanf("%lf%lf", &x[i], &y[i]);//考虑搜索
 		for (int i = 1; i <= n; i++)
	 		for (int j = i + 1; j <= n; j++)
	 			add(i, j);//求出i,j确定的抛物线状态 
	 	for (int i = 0; i < (1 << n); i++)//当前状态是i 
	 		for (int j = 1; j <= n; j++)//给当前状态加上一条抛物线,首先必定会撞到1个点, 
	 		{
	 			dp[i | (1 << j - 1) ] = min(dp[i | (1 << j - 1)], dp[i] + 1);
	 			for (int k = j + 1; k <= n; k++)//再找一个能跟j在同一条抛物线上的点,然后加上此抛物线所确定的状态。 
	 				if (ha[j][k])
	 					dp[i | ha[j][k]] = min(dp[i | ha[j][k]], dp[i] + 1);
			}
	 	printf("%d\n", dp[(1 << n)- 1]);//所有数都选上的最小代价。
 	}
 	return 0;
}
posted @ 2019-11-05 18:47  DAGGGGGGGGGGGG  阅读(104)  评论(0编辑  收藏  举报