题目描述:
2021年的暑假到了,小摩尔们看着手游版波光粼粼的小河,觉得钓鱼会是一个很完美的夏日活动。可是参加活动的摩尔数量太多了,本着爱与和平的原则,为了不让小摩尔们为了争夺场地而吵架,几只聪明的小摩尔决定从一条有着各种各样小鱼的大河修建一个主水道引河水,再通过修建小水道把主水道的河水引到每个小摩尔的家门口。考虑到美观和地形优势,这几只聪明的小摩尔决定按照由东向西的方向修建主水道,而小水道则以由北往南或者由南往北的方向与主水道相连。为了更快竣工,小摩尔们想请你帮帮忙,确定主水道的最优位置并计算出最少需要多久可以修完下水道?
输入格式:
第一行是一个整数n,表示参加钓鱼比赛的摩尔数量(0<n<10000);第二行是一个整数t,表示修建一单位的小水道花费的时间(0<t<10);接下来n行是每位参赛小摩尔的家的位置,每行有两个整数x和y(-10000<x,y<10000),其中x表示东西方向,y表示南北方向。
输出格式:
仅一行,即一个整数,代表最少需要的时间。
样例
样例输入
6
4
3 7
12 5
9 13
20 1
11 11
211 -5
样例输出:
120
数据范围与提示:
0<n<10000
0<t<10
-10000<x,y<10000
根据题意,我们可以以东西主河道作为x轴,南北作为y轴,发现每一条小河道的距离为距离主河道的距离|y|,而所需时间即是|y|乘以单位时间t,与x无关,也就是说,从第三个数开始,每奇数个数读取后不产生作用,计算式不需要考虑x。
所以解答程序有以下几个要点:
1.构造一个循环,一次读入输入的所有数字,并储存。
2.求得y轴上一点,使得该点距离其他点的距离之和(一次线性)最短
3.求得以该点为x轴时各点纵坐标的绝对值之和。
注意:数据范围(这一点会在之后详细阐述)
以下是第一次的代码
#include<stdio.h>
#include<math.h>
int split(long a[], int low, int high)
{
int part_element = a[low];
for (;;) {
while (low < high && part_element <= a[high])
high--;
if (low >= high)
break;
a[low++] = a[high];
while (low < high && a[low] <= part_element)
low++;
if (low >= high) break;
a[high--] = a[low];
}
a[high] = part_element;
return high;
}
void quicksort(long a[], int low, int high)
{
int middle;
if (low >= high)
return;
middle = split(a, low, high);
quicksort(a, low, middle - 1);
quicksort(a, middle + 1, high);
}
int main() {
long n, num, sum, target;
int t;
long x[10];
long y[10];
scanf("%d", &n);
scanf("%d", &t);
for (long i = 0; i < n; i++)
{
x[i] = scanf("%d", &x[i]);
y[i] = scanf("%d", &y[i]);
}
quicksort(y, 0, n - 1);
if (n % 2 == 0)
num = (y[n / 2] + y[n / 2 - 1]) / 2;
else
num = y[(n/2) ] ;
for (long i = 0; i < n; i++) {
sum += abs(y[i] - num);
}
target = sum * t;
printf("%d", target);
return 0;
}
我的思路是,使用一个快速排序算法,将y所储存的数字按照从低到高排序,之后取中位数:
当这一系列数字个数为奇数时,中位数到各点距离之和最短;
当这一系列数字个数为偶数时,最中间两点间任意数字到各点距离之和想等且最短。
然而在devC++上代入样例时发现无法得到预期结果。
分析原因:
最开始希望变长数组能够按照输入的数字改变长度,在声明时使用 int x[t]的形式,报错,再看题,数据范围0<t<10,改为 x[10] .
根据输出结果推测,输出=t,可知y的读取和平均数的计算中有一部分出了错。
于是修改代码,首先使用C++库中的sort函数简化;
其次,根据题意,将数组扩大到10000;
修改代码如下:
#include<iostream>
#include<cmath>
#include<stdio.h>
#include<algorithm>
int main() {
using namespace std;
int n, target;
int t;
int num;
scanf("%d", &n);
scanf("%d", &t);
int a[10000];
int b[10000];
for (int i = 0; i < n; i++)
{
scanf("%d", &a[i]);
scanf("%d", &b[i]);
}
sort(b, b + n);
if (n % 2 == 0)
num = b[n / 2];
else if (n == 1)
return 0;
else
num = ((b[n / 2 - 1] + b[n / 2]) / 2);
int sum = 0;
for (int i = 0; i < n; i++) {
sum += abs(b[i] - num);
}
target = t * sum ;
printf("%d\n", target);
return 0;
}
代码已经可以满足样例,但oj上只有60分。
继续分析:
测试数据部分可以通过,部分不能。怀疑是奇偶数出错,即中位数出问题。
(最后确实是中位数写反了,但那都是后话了)
当时没有觉得算法出了什么问题,而且有的情况会超时,怀疑是数组越界,于是翻书,修改声明。
朋友的建议下使用long long.
之后选择查找的算法,使用三分查找;
修改后代码如下:
# include <cstdio>
# include <cmath>
# include <algorithm>
using namespace std;
const int N = 2e4 + 12;
int n;
long long t,m = 0x7fffffff,y[N];
long long check(long long k)
{
long long ans = 0;
for(int i = 1;i <= n;i++)
ans += abs(y[i] - k);
return ans;
}
int main()
{
scanf("%d %lld",&n,&t);
for(int i = 1;i <= n;i++)
for(int j = 0;j < 2;j++)
scanf("%lld",&y[i]);
sort(y + 1,y + n + 1);
long long l = y[1],r = y[n],mid1,mid2;
while(l + 5 < r)
{
mid1 = l + (r - l) / 3,
mid2 = l + (r - l) / 3 * 2;
if(check(mid1) < check(mid2))r = mid2;
else l = mid1;
}
for(int i = l;i <= r;i++)m = min(m,check(i));
printf("%lld\n",m * t);
}
abs函数用来求绝对值,声明改为long long.
AC.
但后续和学长交流得知,当初理解没问题,后续想复杂了。
感想:
1.分析题目第一重要,虽然看着很像查找但最终思路很简单;
2.代码规范,对C++还不熟悉,代码中c与c++杂糅,需要尽快从c转向c++;
3.多刷刷洛谷,不能再一道题做一天了。
最后附上正解(python) (来自学长)
n = eval(input())
t = eval(input())
y_pos = []
y_max = -1
y_min = 100000000
distance = 100000000
for i in range(n):
temp = input()
temp = int(temp.split(" ")[1])
y_pos.append(temp)
if(temp > y_max):
y_max = temp
if(temp < y_min):
y_min = temp
for i in range(y_min, y_max+1):
temp = 0
No = 0
for j in range(n):
temp += t*abs(y_pos[j]-i)
if(temp < distance):
distance = temp
print(distance)