这题题意说很多个点,某个人在一点要射击这些点,但是子弹是可以以任意方向反射的,但是只能反射一次,也就是说一个人一次可以打两个点。那么问怎么打能使子弹走过的距离总和最短。
枚举状态、和要打的两个点。不过这里会出现重复计算的问题,不解决的话会超时。就是循环的问题,一开始我两重循环枚举,但是会发现很累赘,然后看了下大牛的题解,一个小优化,就是枚举一个点满足条件的然后跳出循环,之后在枚举另外一个点。仔细思考着之间的差别。
#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;
}