• CSUSTOJ-石上优不想留级(一维坐标三分及思维解法)


    题目连接:http://acm.csust.edu.cn/problem/4043
    CSDN食用链接:https://blog.csdn.net/qq_43906000/article/details/109460023

    Description

    众所周知,石上同学由于沉迷游戏,导致成绩惨淡,要是这次考试再不及格,他就要留级了,由于这次考前接受过四宫前辈的教导,如果再不及格,石上一定会死的很惨(因为四宫前辈太恐怖了),于是石上通宵刷题,但是他被一道简单题卡住了,于是他向你求助(因为四宫前辈太可怕,不敢去问),希望你能帮帮他。

    这道题是这样的:在一个三维空间上,有 (n) 个点,每个点的坐标用 ((x_i, y_i, z_i)) 表示, 现在需要你找到一个点 ((x, y, z)) ,使得其他 (n) 个点到这个点的曼哈顿距离最小。

    两个点 (i, j) 之间的曼哈顿距离 (dis) 定义为:(dis = |x_i - x_j| + |y_i - y_j| + |z_i - z_j|)

    (|a - b|) 表示 (a)(b) 的差值的绝对值

    input

    第一行一个整数 (n) , ((1 leq n leq 1e5))

    接下来有 (n) 行,每行 (3) 个数 (x, y, z) ,代表第 (i) 个数的坐标, ((-1e9 leq x,y,z leq 1e9))

    output

    输出一个整数,表示答案

    Sample Input 1
    3
    -5 -10 -9
    -2 7 2
    -9 5 -4
    Sample Output 1
    35

    Sample Input 2
    2
    -3 2 -2
    -2 -5 -5
    Sample Output 2
    11

    emmm,看一下要求的,假设我们找到的点为P那么答案就是:(ans=min(sum_{i=1}^{n}|x_p-x_i|+|y_p-y_i|+|z_p-z_i|)),那么由于都是取绝对值的,所以我们可以直接化为(ans=min(sum_{i=1}^{n}|x_p-x_i|)+min(sum|y_p-y_i|)+min(sum z_p-z_i)),也就是我们只需要解决一个问题就好了,也就是解决求解(min(sum x_p-x_i)),很明显这个结果在一维坐标轴上从左到右他是一个先降后增的。也就是一个二次函数抛物线形的,那么也就可以直接三分求解(x_p)了,其他的同理可得。

    以下是AC代码:

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int mac=1e5+10;
    const int inf=1e9+10;
    
    int x[mac],y[mac],z[mac];
    int n;
    
    ll cal(ll x,int *a)
    {
    	ll ans=0;
    	for (int i=1; i<=n; i++)
    		ans+=abs(x-a[i]);
    	return ans;
    }
    
    ll solve(int *a) {
        ll l = -inf, r = inf;
        while(l < r) {
            ll x1 = floor(1.0 * (2ll * l + r) / 3);
            ll x2 = floor(1.0 * (l + 2ll * r + 2) / 3);
            if(cal(x1,a) < cal(x2,a)) r = x2 - 1;
            else l = x1 + 1;
        }
        return r;
    }
    
    int main(int argc, char const *argv[])
    {
    	scanf ("%d",&n);
    	for (int i=1; i<=n; i++)
    		scanf ("%d%d%d",&x[i],&y[i],&z[i]);
    	ll xp=solve(x);
    	ll yp=solve(y);
    	ll zp=solve(z);
    	ll ans=0;
    	for (int i=1; i<=n; i++)
    		ans+=(abs(xp-x[i])+abs(yp-y[i])+abs(zp-z[i]));
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    当然实际上用不着这么麻烦,实际上有个结论,我们直接取每个坐标的中位数就行了。

    以下是AC代码:

    #include <bits/stdc++.h>
    using namespace std;
    
    #define srt(x) sort(x,x+n)
    const int mac=1e5+10;
    typedef long long ll;
    
    ll x[mac],y[mac],z[mac];
    
    int main(int argc, char const *argv[])
    {
    	int n;
    	scanf ("%d",&n);
    	for (int i=0; i<n; i++)
    		scanf ("%lld%lld%lld",&x[i],&y[i],&z[i]);
    	srt(x);srt(y);srt(z);
    	ll ans=0;
    	for (int i=0; i<n; i++)
    		ans+=abs(x[n/2]-x[i])+abs(y[n/2]-y[i])+abs(z[n/2]-z[i]);
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    Oracle的 listagg() WITHIN GROUP ()函数使用
    AJAX工作原理与缺点
    牛客网数据库SQL实战(此处只有答案,没有表内容)
    Jsp的四大作用域与九大对象
    eclipse的debug调试技巧
    浏览器与服务器交互
    eclipse图标含义
    不要在构造和析构函数中调用虚函数
    构造,析构 cpp
    2 c++对象被使用前要先被初始化
  • 原文地址:https://www.cnblogs.com/lonely-wind-/p/13941890.html
Copyright © 2020-2023  润新知