C - Diamond Miner
题目描述
有 n n n 个矿工在 y y y 轴上, n n n 个钻石矿在 x x x 轴上,原点处(坐标 ( 0 , 0 ) (0,0) (0,0) )什么也没有,每个矿工要负责拿一块钻石矿,每个钻石矿恰好被一个矿工采走。采一个钻石矿的代价为矿工与钻石矿间的直线距离。求总代价的最小值。
数据范围与提示
共有 t t t 组数据;
1 ≤ t ≤ 10 , 1 ≤ n ≤ 1 0 5 , − 1 0 8 ≤ x , y ≤ 1 0 8 1\le t\le 10,1\le n\le 10^5,-10^8\le x,y\le 10^8 1≤t≤10,1≤n≤105,−108≤x,y≤108 ;
n n n 的总和不超过 1 0 5 10^5 105 。
思路
由于一个矿工和一个钻石矿的距离为 y 2 + x 2 \sqrt{y^2+x^2} y2+x2 ,所以实际上和坐标正负无关,所以先把所有坐标都取绝对值。
然后通过画图我们可以知道,交叉的线一定不优:
所以只需要把坐标从小到大排序然后一一对应即可。复杂度 O ( n log n ) O(n\log n) O(nlogn) 。
这题应该没人用非结论做法吧
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#define ll long long
#define MAXN 100005
#define uns unsigned
using namespace std;
inline ll read(){
ll x=0;bool f=1;char s=getchar();
while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+s-'0',s=getchar();
return f?x:-x;
}
int n;
vector<double>a,b;
inline double dis(double c,double d){
return sqrt(c*c+d*d);
}
signed main()
{
for(int T=read();T--;){
n=read();
a.clear(),b.clear();
for(int i=1;i<=(n<<1);i++){
double x=read(),y=read();
if(x<0)x=-x;if(y<0)y=-y;
if(x==0)a.push_back(y);
else b.push_back(x);
}
sort(a.begin(),a.end());
sort(b.begin(),b.end());
double ans=0;
for(int i=0;i<n;i++)ans+=dis(a[i],b[i]);
printf("%.15f\n",ans);
}
return 0;
}