题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3920
题目大意:你在一个位置用激光枪灭敌人,给你初始位置,下面是2*n个敌人的位置,你一枪能杀两个,可以杀死任意两个人,激光束的路径是消耗的能量,求最小能量,保证一次只消灭两个敌人,你的位置不变
Sample Input
2
0 0
1
6 0
3 0
0 0
2
1 0
2 1
-1 0
-2 0
Sample Output
Case #1: 6.00
Case #2: 4.41
分析:给每个点编个号,用状态压缩表示射击那些点,射击过的表示为1,dp[i]表示射击状态 i 时最少消耗,答案即为dp[(1<<2*n)-1]
先每个点到射击点排个序,每次选最近的一个点,和距离这个点最近的点,这两个就是应该选的点(贪心)
代码如下:
# include<cstdio>
# include<cstring>
# include<algorithm>
# include<iostream>
# include<cmath>
using namespace std;
const int INF = 0xffffff;
double dis[][],dp[<<];
int vis[<<];
int n,fx,fy;
struct NODE
{
int x,y;
} node[];
double DIS(double x1,double y1,double x2,double y2)
{
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
bool cmp(NODE a,NODE b)
{
return DIS(a.x,a.y,fx,fy)<DIS(b.x,b.y,fx,fy);
}
double DP(int sta)
{
if(vis[sta])
return dp[sta];
vis[sta] = ;
if(sta==)
dp[]=0.0;
else
{
int i;
for(i=; i<2*n; i++)
{
if((<<i) & sta)break;
}
for(int j=i+; j<*n; j++)
{
if((sta & (<<j))==)continue;
dp[sta]=min(DP(sta^(<<j)^(<<i))+dis[i][j],dp[sta]);
}
}
return dp[sta];
}
int main()
{
int T,cas=;
int i,j;
scanf("%d",&T);
for(cas=; cas<=T; cas++)
{
scanf("%d%d",&fx,&fy);
scanf("%d",&n);
for(i=; i<*n; i++)
scanf("%d%d",&node[i].x,&node[i].y);
sort(node,node+*n,cmp);
for(i=; i<*n; i++)
for(j=i+; j<*n; j++)
{
dis[i][j]=DIS(node[i].x,node[i].y,fx,fy)+DIS(node[i].x*1.0,node[i].y*1.0,node[j].x*1.0,node[j].y*1.0);
}
memset(vis,,sizeof(vis));
memset(dp,INF,sizeof(dp));
printf("Case #%d: %.2f\n",cas,DP((<<(*n))-));
}
return ;
}