华为非AI方向笔试真题【工厂落点最小加权路程】

工厂落点最小加权路程(C++/Py/Java/Js/Go)题解

华为笔试真题 8月26号 非AI方向第一题 100分题型

题目内容

现在要在二维平面上选定一个工厂落点 (P,Q)(P, Q)(P,Q),目标是让所有居民走到工厂的加权曼哈顿距离之和尽可能小。请输出这个最小值。

该地区共有 mmm 个居民区。第 iii 个居民区位于 (ai,bi)(a_i, b_i)(ai,bi),住有 wiw_iwi 名居民。

两点之间的路程按曼哈顿距离计算:(s,t)(s, t)(s,t) 到 (u,v)(u, v)(u,v) 为 ∣s−u∣+∣t−v∣|s-u|+|t-v|∣s−u∣+∣t−v∣。第 iii 个居民区对工厂的加权曼哈顿距离为

wi×(∣ai−P∣+∣bi−Q∣)w_i \times \bigl(|a_i-P|+|b_i-Q|\bigr)wi×(∣ai−P∣+∣bi−Q∣)

解答要求:

  • 时间限制:C/C++ 100100100ms,其他语言:200200200ms
  • 内存限制:C/C++ 256256256MB,其他语言:512512512MB

输入描述

第一行一个整数 mmm(1≤m≤100001 \le m \le 100001≤m≤10000),表示居民区个数。

接下来 mmm 行,每行三个整数 aia_iai、bib_ibi、wiw_iwi(−109≤ai,bi≤109-10^9 \le a_i, b_i \le 10^9−109≤ai,bi≤109,1≤wi≤1061 \le w_i \le 10^61≤wi≤106),表示第 iii 个居民区的坐标和居民人数。

输出描述

输出一个整数,表示最小的加权曼哈顿距离之和。

样例1

输入

复制代码
3
0 3 2
5 3 4
5 7 1

输出

复制代码
14

说明

把工厂建在 (5,3)(5, 3)(5,3) 时:

  • 到 (0,3)(0, 3)(0,3) 的距离是 ∣0−5∣+∣3−3∣=5|0-5|+|3-3|=5∣0−5∣+∣3−3∣=5,人数 222,贡献 5×2=105 \times 2 = 105×2=10
  • 到 (5,3)(5, 3)(5,3) 的距离是 000,人数 444,贡献 000
  • 到 (5,7)(5, 7)(5,7) 的距离是 ∣5−5∣+∣7−3∣=4|5-5|+|7-3|=4∣5−5∣+∣7−3∣=4,人数 111,贡献 444
  • 总和为 10+0+4=1410+0+4=1410+0+4=14,这就是最小的加权曼哈顿距离之和

样例2

输入

复制代码
3
0 1 1
4 1 1
2 6 1

输出

复制代码
9

说明

把工厂建在 (2,1)(2, 1)(2,1) 时:

  • 到 (0,1)(0, 1)(0,1) 的距离是 222,贡献 222
  • 到 (4,1)(4, 1)(4,1) 的距离是 222,贡献 222
  • 到 (2,6)(2, 6)(2,6) 的距离是 555,贡献 555
  • 总和为 2+2+5=92+2+5=92+2+5=9,这就是最小的加权曼哈顿距离之和

题解和思路

思路

实现思路:数学原理

  1. 要求∑i=1mwi(∣ai−P∣+∣bi−Q∣)\sum_{i=1}^{m} w_i\left(\lvert a_i-P\rvert+\lvert b_i-Q\rvert\right)∑i=1mwi(∣ai−P∣+∣bi−Q∣)可以拆分为∑i=1mwi∣ai−P∣+∑i=1mwi∣bi−Q∣\sum_{i=1}^{m}w_i\lvert a_i-P\rvert+\sum_{i=1}^{m}w_i\lvert b_i-Q\rvert∑i=1mwi∣ai−P∣+∑i=1mwi∣bi−Q∣ ,这样就能二维问题转换为一维问题,分别求出最优的P和Q在对应轴上和最小。
  2. 而一维问题的解决方案就是加权中位数,以x轴为例,计算对应轴上总权重,将所有ai从小到大排序,将坐标从小到大移动,当左侧累计权重第一次达到总权重一半的位置,就是对应轴能取的最优位置。
  3. 按照1、2计算出对应P 和 Q,然后计算权重即可。
  4. 算法总体时间复杂度为O(logm)

C++

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

using ll = long long;

struct Node {
    // 位置
    ll pos;
    // 权重
    ll w;
};
// 寻找加权中位数
ll getMedian(vector<Node> v) {
    // 坐标升序
    sort(v.begin(), v.end(), [](const Node& a, const Node& b) {
        return a.pos < b.pos;
    });
    ll total = 0;
    for (auto &x : v) {
        total += x.w;
    }
    ll sum = 0;
    for (auto &x : v) {
        sum += x.w;
        if (sum * 2 >= total) {
            return x.pos;
        }
    }
    return 0;
}

int main() {
    int m;
    cin >> m;
    vector<Node> xs(m), ys(m);
    
    for (int i = 0; i < m; i++) {
        ll a,b,w;
        cin >> a >> b >> w;
        xs[i] = {a, w};
        ys[i] = {b, w};
    }
    ll P = getMedian(xs);
    ll Q = getMedian(ys);
   
    ll ans = 0;
    for (int i = 0; i < m; i++) {
        ans += xs[i].w * llabs(xs[i].pos - P);
        ans += ys[i].w * llabs(ys[i].pos - Q);
       
    }
    cout << ans;
    return 0;
}

Java

java 复制代码
import java.io.*;
import java.util.*;

public class Main {
    static class Node {
        // 位置
        long pos;
        // 权重
        long w;

        Node(long pos, long w) {
            this.pos = pos;
            this.w = w;
        }
    }

    // 寻找加权中位数
    static long getMedian(Node[] v) {
        // 坐标升序
        Arrays.sort(v, Comparator.comparingLong(a -> a.pos));

        long total = 0;
        for (Node x : v) {
            total += x.w;
        }

        long sum = 0;
        for (Node x : v) {
            sum += x.w;
            if (sum * 2 >= total) {
                return x.pos;
            }
        }

        return 0;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        int m = sc.nextInt();
        Node[] xs = new Node[m];
        Node[] ys = new Node[m];

        for (int i = 0; i < m; i++) {
            long a = sc.nextLong();
            long b = sc.nextLong();
            long w = sc.nextLong();

            xs[i] = new Node(a, w);
            ys[i] = new Node(b, w);
        }

        long P = getMedian(xs);
        long Q = getMedian(ys);

        long ans = 0;
        for (int i = 0; i < m; i++) {
            ans += xs[i].w * Math.abs(xs[i].pos - P);
            ans += ys[i].w * Math.abs(ys[i].pos - Q);
        }

        System.out.println(ans);
    }
}

python

python 复制代码
import sys

# 寻找加权中位数
def get_median(v):
    # 坐标升序
    v.sort(key=lambda x: x[0])

    total = 0
    for pos, w in v:
        total += w

    sum_w = 0
    for pos, w in v:
        sum_w += w
        if sum_w * 2 >= total:
            return pos

    return 0


data = list(map(int, sys.stdin.buffer.read().split()))
idx = 0

m = data[idx]
idx += 1

xs = []
ys = []

for _ in range(m):
    a = data[idx]
    b = data[idx + 1]
    w = data[idx + 2]
    idx += 3

    # 位置、权重
    xs.append([a, w])
    ys.append([b, w])

P = get_median(xs)
Q = get_median(ys)

ans = 0
for i in range(m):
    ans += xs[i][1] * abs(xs[i][0] - P)
    ans += ys[i][1] * abs(ys[i][0] - Q)

print(ans)

Javascript

js 复制代码
const readline = require('readline');

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

let input = [];

rl.on('line', line => {
    input.push(...line.trim().split(/\s+/));
});

rl.on('close', () => {
    let idx = 0;

    const m = Number(input[idx++]);
    const xs = [];
    const ys = [];

    for (let i = 0; i < m; i++) {
        const a = BigInt(input[idx++]);
        const b = BigInt(input[idx++]);
        const w = BigInt(input[idx++]);

        xs.push({ pos: a, w: w });
        ys.push({ pos: b, w: w });
    }

    // 寻找加权中位数
    function getMedian(v) {
        // 坐标升序
        v.sort((a, b) => a.pos < b.pos ? -1 : a.pos > b.pos ? 1 : 0);

        let total = 0n;
        for (const x of v) {
            total += x.w;
        }

        let sum = 0n;
        for (const x of v) {
            sum += x.w;
            if (sum * 2n >= total) {
                return x.pos;
            }
        }

        return 0n;
    }

    const P = getMedian(xs);
    const Q = getMedian(ys);

    let ans = 0n;

    for (let i = 0; i < m; i++) {
        ans += xs[i].w * abs(xs[i].pos - P);
        ans += ys[i].w * abs(ys[i].pos - Q);
    }

    console.log(ans.toString());

    function abs(x) {
        return x < 0n ? -x : x;
    }
});

Go

go 复制代码
package main

import (
	"bufio"
	"fmt"
	"os"
	"sort"
)

type Node struct {
	// 位置
	pos int64
	// 权重
	w int64
}

// 寻找加权中位数
func getMedian(v []Node) int64 {
	// 坐标升序
	sort.Slice(v, func(i, j int) bool {
		return v[i].pos < v[j].pos
	})

	var total int64
	for _, x := range v {
		total += x.w
	}

	var sum int64
	for _, x := range v {
		sum += x.w
		if sum*2 >= total {
			return x.pos
		}
	}

	return 0
}

func abs(x int64) int64 {
	if x < 0 {
		return -x
	}
	return x
}

func main() {
	in := bufio.NewReader(os.Stdin)
	out := bufio.NewWriter(os.Stdout)
	defer out.Flush()

	var m int
	fmt.Fscan(in, &m)

	xs := make([]Node, m)
	ys := make([]Node, m)

	for i := 0; i < m; i++ {
		var a, b, w int64
		fmt.Fscan(in, &a, &b, &w)

		xs[i] = Node{a, w}
		ys[i] = Node{b, w}
	}

	P := getMedian(xs)
	Q := getMedian(ys)

	var ans int64
	for i := 0; i < m; i++ {
		ans += xs[i].w * abs(xs[i].pos-P)
		ans += ys[i].w * abs(ys[i].pos-Q)
	}

	fmt.Fprintln(out, ans)
}
相关推荐
梦想不只是梦与想1 小时前
鸿蒙 应用类型:企业应用
华为·harmonyos·企业应用
residual_fan1 小时前
数据缺失填补算法之CUR-Estimator
算法·数据挖掘·数据分析
wabs6661 小时前
关于二叉树【力扣107.二叉树的层序遍历II的思考】
数据结构·c++·算法·leetcode·二叉树·层序遍历
大雷神1 小时前
HarmonyOS ArkGraphics 2D 文本绘制实操:用 TextBlob 做动态文字排版
华为·harmonyos
一切皆是因缘际会2 小时前
资源的适配
大数据·人工智能·算法
鲸能云2 小时前
【智慧能源】源储调售一体化架构与 EMS 滚动调度算法实践 —— 工商业光储收益提升 22% 的技术拆解
算法·架构·能源
双翌视觉2 小时前
曝光调不对,算法全白费:机器视觉的曝光三态解析
人工智能·算法·计算机视觉
Swift社区2 小时前
鸿蒙 App 如何设计 Memory Center?一文讲透 Agent 的长期记忆架构
华为·架构·harmonyos
ysu_03142 小时前
02-单链表完全指南
c语言·数据结构·算法·leetcode