23. 良乡供暖站

description

若干年以后,良乡校区已经全部建设完毕。有许多的建筑和道路,它们纵横交错形成一个 的棋盘式网状布局。这个棋盘中的每一个横行和纵行均为一条道路,在某些道路交叉点上具有一栋建筑。每一栋建筑可以由一个二维坐标 表示,表示这一栋建筑处于第 横行、第 纵行的两条道路的交叉点上。

现在,良乡要在某一个道路交叉点上(可能已经有建筑)新建立一个供暖站。新的供暖站将修建供热管道到已有的每一栋建筑。为了方便维修,供暖管道只能直接修建在道路之下。为了最小化成本,良乡管理处想要让要修建的管道的总长度最小。

假定这个道路“棋盘”是单位均匀的(也就是每一个交叉点到相邻的四个交叉点之间的距离均为1)。现在良乡管理处想要知道,他们最少需要修建多长的管道。

输入描述

输入文件第一行为一个整数 ,表示已有建筑的数量。

接下来的 行,每行包含两个以空格分隔的整数 ,分别表示每一栋建筑的坐标。

输出描述

输出一行一个整数表示需要新修建的管道的最短长度。

样例说明

对于第一组测试用例,供暖站应该建在坐标 (0, 0) 处,其到三个已有建筑的距离分别为 2, 2, 0,因此总距离为 4;

对于第二组测试用例,供暖站直接建在 (50, 50) 处即可。

 测试输入 期待的输出 时间限制 内存限制 额外进程
测试用例 1以文本方式显示
  1. 3↵
  2. 0 2↵
  3. 2 0↵
  4. 0 0↵
以文本方式显示
  1. 4↵
1秒64M0
测试用例 2以文本方式显示
  1. 1↵
  2. 50 50↵
以文本方式显示
  1. 0↵
1秒64M0

code

  1. 管道要通到每一栋建筑,那么供暖站建在现有建筑上一定是最短的(但不是说一定要建在现有建筑上才能最短,可以建在路径上),建在任何一个建筑下都不会影响管道长度,因为只需要输出最短长度,那么直接利用现有建筑坐标作为切入点
  2. 题目说管道只建在道路下面,且是单位距离,那就相当于把x和y分割开来了,两个建筑物之间的距离dis = |x'-x|+|y'-y|;
  3. 可以各自研究
  4. 只需要进行排序,然后选择中间的那一个建筑物作为供暖站即可
  5. 排序我使用的是归并排序,如果不是很理解我的代码可以看看这个先 https://editor.csdn.net/md/?articleId=121308155
  6. 特别注意
    1). 管道不可以共用,即使两个建筑物在同一条直线上
    2). 相同的x或者y也要累加,不能只算一次

代码如下

/******************************************
 * @Author       : 鱼香肉丝没有鱼
 * @Date         : 2021-10-28 11:06:13
 * @LastEditors  : 鱼香肉丝没有鱼
 * @LastEditTime : 2021-11-15 15:40:37
 ******************************************/

#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;

//此题中未使用
template <typename T>
void PrintArray(T arr[], int n)
{
    int i;
    for(i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
}

//使用模板,不止适用于整型数组,浮点型等其他也可以
template <typename T>  //在这了typename和class没有区别
void MergeSort(T arr[], int n)
{
    int step, left_min, left_max, right_min, right_max;  //步长,两个子串的左右边界,区间是左开右闭
    int cnt;  // temp数组的下标
    T* temp = new T[n];  //辅助数组,和原数组一样长

    for(step = 1; step < n; step += step) {  // step是步长,也就是区间长度
        for(left_min = 0; left_min < n - step; left_min = right_max) {
            right_min = left_max = left_min + step;  //右边界等于左边界+区间长度
            right_max = right_min + step;

            if(right_max > n)
                right_max = n;  //不能超出数组原本的长度嘛
            cnt = 0;
            while(left_min < left_max && right_min < right_max) {  //只要有一边遍历完就退出
                // 左右两边谁小取谁存入temp中,然后各自下标递增
                if(arr[left_min] < arr[right_min])
                    temp[cnt++] = arr[left_min++];
                else
                    temp[cnt++] = arr[right_min++];
            }
            /*上面的while循环如果退出那么一定是左右两边其中一个子串遍历完了
            但是另一个子串可能还有剩余元素没有遍历。假如右边剩,因为每次取的都是较小的值,
            如果右边剩的话那就不用管了,本来就是应该放在右边的,只用考虑左边剩的情况
            */
            while(left_min < left_max)  //右边剩的话不会进入这个while循环
                arr[--right_min] = arr[--left_max];  //把左边的剩下元素放在右边,注意这时候rightmin指向
                                                     // 的是rightmax的位置,使用前都要自减
            while(cnt > 0)
                arr[--right_min] = temp[--cnt];  // tmep中的元素是有序的,现在还原到arr中,注意下标是先自减
        }
    }

    delete[] temp;
}

//累加管道长度
long long int GetDistance(int* arr, int n)
{
    long long dis = 0;
    int mid = 0;

    mid = n / 2;
    for(register int i = 0; i < n; i++) {
        dis += fabs(arr[mid] - arr[i]);
    }
    return dis;
}

int main()
{
    int n;

    int* row = NULL;  //存储行
    int* line = NULL;  //存储列

    long long disX = 0;
    long long disY = 0;
    freopen("file in.txt", "r", stdin);
    cin >> n;
    row = new int[n];
    line = new int[n];

    for(register int i = 0; i < n; i++)
        scanf("%d %d", &row[i], &line[i]);

    MergeSort(row, n);  //归并排序
    MergeSort(line, n);

    disX = GetDistance(row, n);
    disY = GetDistance(line, n);
    printf("%lld\n", disX + disY);

    delete[] row;
    delete[] line;
    return 0;
}

summary

  1. 注意一点,供暖站应该修在中间那个建筑物上,而不是建在中心距离上,是不一样的,可以看一下下面这个代码和上面程序在mid的处理上有什么不一样
long long int GetDistance(int* arr, int n)
{
    long long dis = 0;
    long long int mid = 0;

    mid = (arr[0]+arr[n-1]) / 2;
    for(register int i = 0; i < n; i++) {        
        dis += fabs(mid - arr[i]);
    }
    return dis;
}
  1. 遇到了一个关于printf不明确的魔幻报错,整理在这了 vscode 提示printf不明确
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值