存点后排序,1<<20枚举每个状态,每次找到这个状态中里原点最近没有击落的子弹的点,然后枚举击落这个子弹后下一个没有被击落的点。
状态转移: tmp = i|(1<<j)|(1<<k),dp[tmp]=min(dp[tmp],dp[i]+dist[j]+dist[j][k])
当i没有被更新过时要continue 这个题很卡时间。
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int inf = 0x3f3f3f;
typedef pair<int,int>P;
int px,py,n;
double dp[(1<<20)+10];
struct node{
int x,y;
double dist;
bool operator < (const node& T) const{
return dist<T.dist;
}
}v[23];
double cla(int x1,int y1,int x2,int y2)
{
double s = (x1-x2)*(x1-x2)*1.0+(y1-y2)*(y1-y2)*1.0;
return sqrt(s);
}
int main()
{
int cases;
int t = 1;
scanf("%d",&cases);
while(cases--)
{
scanf("%d%d",&px,&py);
scanf("%d",&n);
for(int i=0;i<2*n;i++){
scanf("%d%d",&v[i].x,&v[i].y);
v[i].dist = cla(v[i].x,v[i].y,px,py);
}
sort(v,v+2*n);
int maxn = 1<<(2*n);
for(int i=1;i<maxn;i++) dp[i] = inf*1.0;
dp[0] = 0;
for(int i=0;i<maxn;i++)
{
if(dp[i]==inf*1.0) continue;
int j=0;
while(i&(1<<j)&&j<2*n) j++;
for(int k=j+1;k<2*n;k++)
{
if(!(i&(1<<k)))
{
int tmp = i|(1<<j)|(1<<k);
dp[tmp] = min(dp[tmp],dp[i]+v[j].dist+cla(v[j].x,v[j].y,v[k].x,v[k].y));
}
}
}
printf("Case #%d: ",t++);
printf("%.2lf\n",dp[(1<<2*n)-1]);
}
return 0;
}