【vijos】【二分图带权匹配】拯救世界-星际大战
描述
外星人逐渐逼近,为了保护地球,现在决定直接在外空进行战斗。
现在我们有N个导弹。需要在最短的时间内,用这N个导弹摧毁敌方n个目标(1个导弹只能摧毁1个目标)。N个导弹和目标的位置不一定相同,但是给每个导弹确定目标是一件很麻烦的事情。请你编程帮助给每个导弹确定目标,使每个导弹到其目标的距离之和最小。
格式
输入格式
第一行输入N(N<=20)
接下来N行每行包含一个坐标(x,y),表示一个导弹,-10000
输出格式
每个导弹到其目标距离之和的最小值。结果保留3位小数。
代码
#include <cstdio>
#include <cstring>
#include <cmath>
#define INF 0x3f
#define maxn 25
using namespace std;
struct Po{
int x,y;
Po(int x=0,int y=0){
this->x=x; this->y=y;
}
};
int nx,ny,linky[maxn];
double lack,w[maxn][maxn],lx[maxn],ly[maxn];
bool visx[maxn],visy[maxn];
int n;
Po mi[maxn],tar[maxn];
double dist(Po x,Po y){
return sqrt(pow(x.x-y.x,2.0)+pow(x.y-y.y,2.0));
}
bool find(int x){
visx[x]=true;
for(int i=1;i<=ny;i++){
if(visy[i]) continue;
double t=lx[x]+ly[i]-w[x][i];
if(t<1e-5){
visy[i]=true;
if(linky[i]==-1||find(linky[i])){
linky[i]=x;
return true;
}
}else if(t<lack-1e-5) lack=t;
}
return false;
}
double KM(){
memset(linky,-1,sizeof(linky));
for(int i=1;i<=ny;i++) ly[i]=0.000;
for(int i=1;i<=nx;i++){
lx[i]=INF;
for(int j=1;j<=ny;j++)
if(w[i][j]-1e-5>lx[i]) lx[i]=w[i][j];
}
for(int x=1;x<=nx;x++){
while(true){
memset(visx,0,sizeof(visx));
memset(visy,0,sizeof(visy));
lack=INF;
if(find(x)) break;
for(int i=1;i<=ny;i++){
if(visx[i]) lx[i]-=lack;
if(visy[i]) ly[i]+=lack;
}
}
}
double ans=0.000;
for(int i=1;i<=ny;i++)
ans-=w[linky[i]][i];
return ans;
}
int main(){
//freopen("in.txt","r",stdin);
scanf("%d",&n); nx=ny=n;
for(int i=1;i<=n;i++) scanf("%d%d",&mi[i].x,&mi[i].y);
for(int i=1;i<=n;i++) scanf("%d%d",&tar[i].x,&tar[i].y);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
w[i][j]=-dist(mi[i],tar[j]);
printf("%.3f",KM());
return 0;
}