邮局选址

题目描述

在一个镇上有n个村子,每个村子住着数量不等的居民。这些村子可以看做是排列在一条直线上的若干个整数点。现在镇长要在村子里建一个邮局。请问将邮局建在哪一个村子,可以使得所有居民走到邮局的距离和最小。

输入格式

第一行一个整数n,表示有n个村子。

接下来有n行,每行两个数,表示第i个村子的坐标和居民数。保证所有村子的坐标都是正整数。

输出格式

输出两个数,第一个数表示建邮局的村子的坐标,第二个数表示最小的距离和。如何有多个村子适合建邮局,输出坐标最小的那个。输出结果中间有1个空格间隔。

样例
样例输入
4
1 10
5 20
3 5
10 7
样例输出
5 85

做法:
设在 x x x位置建邮局,答案就是 ∑ i = 1 n ∣ x − a i ∣ \sum_{i=1}^{n} |x-a_{i}| i=1nxai,然后分类讨论去绝对值符号,具体实现就是先排序,再用前缀和对每个位置都算一次答案,最后取 m i n min min

代码:

#include <bits/stdc++.h>
using namespace std;
#define MAXN 1000005
#define int long long
struct node {
    int x, cnt;
    node(int a = 0, int b = 0){x = a, cnt = b;}
    bool operator <(const node &t) const {
        return x < t.x;
    }
};
node a[MAXN];
int precnt[MAXN], lascnt[MAXN];
int preans[MAXN], lasans[MAXN];
signed main()
{
    int n;
    scanf("%lld", &n);
    for(int i = 1; i <= n; i++)
        scanf("%lld %lld", &a[i].x, &a[i].cnt);
    sort(a + 1, a + n + 1);
    // puts("-------------------");
    // for(int i = 1; i <= n; i++)
    // {
    //     printf("%d %d\n", a[i].x, a[i].cnt);
    // }
    // puts("-------------------");
    for(int i = 1; i <= n; i++)
        precnt[i] = precnt[i - 1] + a[i].cnt;
    for(int i = n; i >= 1; i--)
        lascnt[i] = lascnt[i + 1] + a[i].cnt;
    for(int i = 2; i <= n; i++)
        preans[i] += preans[i - 1] + precnt[i - 1] * (a[i].x - a[i - 1].x);
    for(int i = n - 1; i >= 1; i--)
        lasans[i] += lasans[i + 1] + lascnt[i + 1] * (a[i + 1].x - a[i].x);
    // for(int i = 1; i <= n; i++)
    // {
    //     printf("%d %d\n", precnt[i], lascnt[i]);
    // }
    // puts("-------------------");
    // for(int i = 1; i <= n; i++)
    // {
    //     printf("%d %d\n", preans[i], lasans[i]);
    // }
    // puts("-------------------");
    int x, ans = 0x3f3f3f3f3f3f3f3f;
    for(int i = 1; i <= n; i++)
    {
        if(preans[i] + lasans[i] < ans)
        {
            ans = preans[i] + lasans[i];
            x = i;
        }
    }
    printf("%lld %lld\n", a[x].x, ans);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值