铺设油井管道问题描述:
某石油公司有n口油井,为方便输送石油,计划修建输油管道。根据设计要求,水平方向有一条主管道,每口油井修一条垂直方向的支线管道通向主管道。请设计一种算法确定主管道的位置,使得所有油井到主管道之间的支线管道长度的总和最小。提示:复杂度为O(n)才能通过所有测试用例。
输入格式:
每个输入文件为一个测试用例,每个文件的第一行给出一个正整数n(1≤n≤1000000),表示油井数量,从第二行起的n行数据,表示每口油井的位置,每行包含以空格分隔的两个整数,分别表示每口油井的横坐标x(−10000≤x≤10000)和纵坐标y(−10000≤y≤10000)。
输出格式:
输出各油井到主管道之间的支管道最小长度总和。
求解思路
首先我们看完题目发现此题给的油井横坐标没有用,所以不用管它,然后要想求出所有油井到主管道之间的管道长度总和最小,就是求每个油井的纵坐标到主管道纵坐标的距离之和最小。然后根据数学知识我们可以得知,当主管道纵坐标为所有油井纵坐标所构成的数列的中位数时最小,所以我们只需要求出这组纵坐标数据的中位数即可。依照题目提示,时间复杂度得小于等于O(n)才行,所以我们选择用计数排序的方法
#include<iostream>
#include<algorithm>
#include<cmath>
#include<string>
#include<iomanip>
#define ll long long
#define endl "\n"
using namespace std;
const int maxn = 1000010;
const int inf = 0x3f3f3f3f;
int t;
int n;
ll ans = 0;//距离总和
int a[20010];
int num[maxn];
int main() {
ios::sync_with_stdio(0);
cin.tie(0);//关流使输入输出更快
cin >> n;
for (int i = 0; i < n; i++) {
int x, y;
cin >> x >> y;
a[y + 10000]++;//利用这里和后面的for循环实现对纵坐标的排序
}
int h=0;
for (int j = 0; j < 20010; j++) {
if (a[j] != 0) {
for (int i = 0; i < a[j]; i++)//部分油井可能有相同的纵坐标所以得用循环以免遗漏
num[h++] = j - 10000;//用来存排序后的油井纵坐标
}
}
for (int i = 0; i< n; i++) {
ans += abs(num[n / 2] - num[i]);//abs用于求绝对值
}
cout << ans;
return 0;
}