hdu3920(状压dp)

这题题意说很多个点,某个人在一点要射击这些点,但是子弹是可以以任意方向反射的,但是只能反射一次,也就是说一个人一次可以打两个点。那么问怎么打能使子弹走过的距离总和最短。

枚举状态、和要打的两个点。不过这里会出现重复计算的问题,不解决的话会超时。就是循环的问题,一开始我两重循环枚举,但是会发现很累赘,然后看了下大牛的题解,一个小优化,就是枚举一个点满足条件的然后跳出循环,之后在枚举另外一个点。仔细思考着之间的差别。

#include<iostream>
#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<math.h>
using namespace std;
typedef long long lld;
typedef unsigned int ud;
#define oo 0x3f3f3f3f
double dp[(1 << 20) + 1];
double dis[22][22];
int st[(1 << 20) + 1];

struct NODE
{
	double x, y;
};
NODE Evil[22];

double Distance(NODE a, NODE b){
	return sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y));
}

int main()
{
	int n, T;
	scanf("%d", &T);
	for (int cas = 1; cas <= T; cas++)
	{
		scanf("%lf %lf", &Evil[0].x, &Evil[0].y);
		scanf("%d", &n);
		n *= 2;
		for (int i = 1; i <= n; i++)
		{
			scanf("%lf %lf", &Evil[i].x, &Evil[i].y);
			dis[0][i] = Distance(Evil[0], Evil[i]);
		}
		for (int i = 1; i <= n; i++)
		for (int j = i + 1; j <= n; j++)
			dis[i][j] = dis[j][i] = Distance(Evil[i], Evil[j]);
		
		int now = 1 << n;
		for (int i = 0; i <= now; i++)
			dp[i] = oo;
		dp[0] = 0.0;
		int i, j, k;
		for (i = 0; i < now; i++)
		{
			if (dp[i] >= (double)oo) continue;

			for (j = 1; j <= n; j++)
			{
				if (!(i & (1 << (j - 1)))) break;
			}
			for (k = j + 1; k <= n; k++)
			{
				if (i&(1 << (k - 1))) continue;

				int sta = i | (1 << (j - 1)) | (1 << (k - 1)); 
				dp[sta] = min(dp[sta], dp[i] + dis[0][j] + dis[j][k]);
				dp[sta] = min(dp[sta], dp[i] + dis[0][k] + dis[k][j]);
			}
		}
		printf("Case #%d: %.2lf\n", cas, dp[now - 1]);
	}
	return 0;
}

你可能感兴趣的:(dp,HDU)