51NOD距离之和最小三个版本的解析

40 篇文章 9 订阅

这三个版本根据难易程度依次排序:

都是线段上一点到其余各点的距离和最小,或者是其变形。

1096 - 距离之和最小

这是个基础题,很明显,这个点应该选在中位数。依据是这个绝对值不等式 |xp|+|yp||xy| | x − p | + | y − p | ≥ | x − y |

#include <bits/stdc++.h>

using namespace std;

using LL = long long;

int main()
{
    for (int n; EOF != scanf("%d", &n); ) {
        vector<LL> points(n);
        for (int i = 0; i < n; scanf("%lld", &points[i++])) {}
        sort(points.begin(), points.end());
        LL mid = points[n / 2];
        printf("%lld\n", accumulate(points.begin(), points.end(), 0LL, [&mid] (LL ans, LL ele) {
                            return ans + abs(mid - ele);
                        }));
    }
    return 0;
}

1108 - 距离之和最小V2

求一个空间点到其余各点的曼哈顿距离的最小和。看起来好像和第一题很想啊,第一题选中位数,那么类比,这个空间点应该选在其余各点组成的凸立方体(凸体,类似凸包)的内部,没错,是的。

但是,好像这个点只是那么直观地可以确定啊,于是,我们换一个角度,空间曼哈顿距离的定义是 |x1x2|+|y1y2|+|z1z2| | x 1 − x 2 | + | y 1 − y 2 | + | z 1 − z 2 | ,先不看 y,z y , z ,那么这个问题是不是又和上面一个问题一样了,而且 x x 是不会影响y,z的,因此这个题只需要对 x,y,z x , y , z 做上面的算法就行了。

#include <bits/stdc++.h>

using namespace std;

using LL = long long;

LL solve(vector<LL> &points)
{
    sort(points.begin(), points.end());
    LL mid = points[points.size() / 2];
    return accumulate(points.begin(), points.end(), 0LL, [&mid] (LL ans, LL ele) {
                        return ans + abs(mid - ele);
                    });
}

int main()
{
    for (int n; EOF != scanf("%d", &n); ) {
        vector<vector<LL> > arr(3, vector<LL>(n));
        for (int i = 0; i < n; scanf("%lld%lld%lld",
                &arr[0][i], &arr[1][i], &arr[2][i]), ++i) {}
        LL ans = 0;
        for (auto &v : arr)
            ans += solve(v);
        printf("%lld\n", ans);
    }
    return 0;
}

1110 - 距离之和最小V3

这个题初看起来不怎么好做,但是注意到题中的 W[i]>0 W [ i ] > 0

这个题的关键就是拆点,把权值看成点数,也就是说有 W[i] W [ i ] 个点重合在 pos[i] p o s [ i ] ,这样子这个题是不是又回到了第一题!

#include <bits/stdc++.h>

using namespace std;

using LL = long long;

int main()
{
    for (int n; EOF != scanf("%d", &n); ) {
        vector<pair<LL, LL> > arr(n);
        for (int i = 0; i < n; scanf("%lld%lld", &arr[i].first, &arr[i].second), ++i) {}
        sort(arr.begin(), arr.end());
        LL sum = accumulate(arr.begin(), arr.end(), 0LL, [] (LL sum, pair<LL, LL> &ele) {
                                return sum + ele.second;
                            });
        auto it = find_if(arr.begin(), arr.end(), [sum] (pair<LL, LL> &ele) {
                            static LL index = 0;
                            return (index += ele.second) > sum / 2;
                        });
        printf("%lld\n", accumulate(arr.begin(), arr.end(), 0LL, [&it] (LL sum, pair<LL, LL> &ele) {
                                        return sum + abs(ele.first - it->first) * ele.second;
                                    }));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值