今日头条 2018 AI Camp 6 月 2 日在线笔试编程题第二道——两数差的和

题目

给 n 个实数 a_1, a_2 ... a_n, 要求计算这 n 个数两两之间差的绝对值下取整后的和是多少。

输入描述

第一行为一个正整数 n 和一个整数 m。接下来 n 行,第 i 行代表一个整数 b_i。a_i = b_i / m, i = 1...n。
n <= 1000: 5分
n <= 100000且 a_i 均为整数: 15分
n <= 100000 1 <= m <= 10^9 0 <= b_i <= 10^18: 25分

输出描述

一个整数

示例1
  • 输入

3 10
11
22
30

  • 输出

2

备注:

请选手在解题时注意精度问题
a_i 取值为 1.1,2.2 和 3.0
|1.1-2.2| 下取整为 1
|1.1-3.0| 下取整为 1
|2.2-3.0| 下取整为 0

思路

  • 1 . 暴力破解法。 直接遍历所有的情况,对两个数相减向下取整然后求和,时间复杂度为 O(n^2)。但要注意,浮点数在内存中的特殊表示方式,比如 1.66666666666666667 - 2.66666666666666667 向下取整后可能为 0,会造成计算错误。
  • 2 . 如果所有的数为整数,先对所有的数据按照升序排列。以 1 2 3 4 5 举例说明,|1-3| = |1-2+2-3| = |1-2| + |2-3|,|1-5| = |1-2+2-3+3-4+4-5| = |1-2| + |2-3| + |3-4| + |4-5|。可以看到,任意两个数的差都可以通过相邻两个数的差转换而来,因此,我们只需要统计相邻两个数的差总共被利用了多少次即可 。|1-2| 的差被 (1,2),(1,3),(1,4)(1,5) 共利用了 4 次{=1×4},|2-3| 的差被 (1,3),(1,4)(1,5),(1,3),(1,4)(1,5) 共利用了 6 次{=2×3},即某个区间的差值被利用的次数等于此区间左边数的个数乘以此区间右边数的个数,总的和就等于每段区间的差乘以利用的次数再求和。
  • 3 . 如果不是整数,有小数部分的话,我们再来看看会怎样。如果小数部分也是升序,比如 |1.1 - 2.2| 和 |1 - 2| 、|3.0 - 10.9| 和 |3 - 10| 取整后都是一样的。但如果小数部分,有降序的,比如 |1.2 - 2.1| 和 |1 - 2| 、|3.9 - 10.0| 和 |3 - 10|,前者就比后者取整后少了 1。因此,小数部分每有一个逆序的,也就是前面比后面大的,总的和就减去 1 即可。
  • 4 . 综上所述,我们可以将数字拆分为整数部分和小数部分,依据整数部分对数据升序排列,同时小数部分与整数部分作一致的调整。然后对整数部分相邻区间求差值并乘以对应的系数先求出总和,再求出小数部分的逆序对个数,总和减去逆序对数即为最终所求。其中,排序和求逆序对数的时间复杂度都可以做到 O(nlogn),因此算法总体复杂度也为 O(nlogn)。

代码实现{排序通过快排,求逆序对数采用归并排序}

// int 4 字节,long 8 字节
#include <iostream>
#include <cmath>
#include <stdio.h>

using namespace std;

void Quick_Sort(long data[], double frac[], int left, int right);
void Merge_Array(double data[], int left, int mid, int right, double temp[]);
void Merge_Sort(double data[], int left, int right, double sorted_data[]);

int cnt = 0; // 逆序对数

int main()
{
    int n = 0, m = 0;
    cin >> n >> m;

    long b[n] = {0};
    long inte[n] = {0}; // a_i 的整数部分
    double frac[n] = {0}; // a_i 的小数部分
    int i = 0;

    double data[n] = {0};
    for (i = 0; i < n; i++)
    {
        cin >> b[i];
        data[i] = double(b[i]) / m;
    }

    // 暴力求解
    long sum1 = 0;
    int j = 0;
    for (i = 0; i < n-1; i++)
    {
        for (j = i+1; j < n; j++)
        {
            sum1 += floor(fabs(data[i] - data[j]));
        }
    }
    cout << sum1 << endl;

    //  分别求出整数部分和小数部分
    for (i = 0; i < n; i++)
    {
        inte[i] = b[i] / m;
        frac[i] = double(b[i]) / m - inte[i];
    }

    // 整数部分升序排列,小数部分随整数部分同步调整
    Quick_Sort(inte, frac, 0, n-1);

    // 相邻区间差乘以系数求总和
    int error[n-1] = {0};
    long sum = 0;
    for (i = 0; i < n-1; i++)
    {
        error[i] = abs(inte[i] - inte[i+1]);
        sum += (i+1) * (n-i-1) * error[i];
    }

    // 归并排序的同时求得小数部分逆序对数
    double sorted_data[n] = {0};
    Merge_Sort(frac, 0, n-1, sorted_data);

    sum -= cnt;
    cout << sum << endl;
    return 0;
}

void Quick_Sort(long data[], double frac[], int left, int right)
{
    if (left < right)
    {
        int i = left, j = right;
        long choice = data[i];
        double temp = frac[i];
        while(i < j)
        {
            while(i < j && data[j] >= choice)
            {
                j--;
            }
            if(i < j)
            {// 小数部分随整数部分同步调整
                data[i] = data[j];
                frac[i] = frac[j];
                i++;
            }
            while(i < j && data[i] <= choice)
            {
                i++;
            }
            if(i < j)
            {// 小数部分随整数部分同步调整
                data[j] = data[i];
                frac[j] = frac[i];
                j--;
            }
        }
        data[i] = choice;//i=j 小数部分随整数部分同步调整
        frac[i] = temp;//i=j
        Quick_Sort(data, frac, left, i-1);
        Quick_Sort(data, frac, i+1, right);
    }
}

// O(n(logn))
void Merge_Sort(double data[], int left, int right, double sorted_data[])
{
    if(left < right)
    {
        int mid = (left + right) / 2;
        Merge_Sort(data, left, mid, sorted_data);
        Merge_Sort(data, mid+1, right, sorted_data);
        Merge_Array(data, left, mid, right, sorted_data);
    }
}

void Merge_Array(double data[], int left, int mid, int right, double temp[])
{
    int i = left, j = mid + 1;
    int k = 0;

    while(i <= mid && j <= right)
    {
        if (data[i] < data[j] || fabs(data[i] - data[j]) <= 1e-9)
        {
            temp[k++] = data[i++];
        }
        else // 左边序列某个数大于右边某个数,则左边序列此数后面的数均大于右边的数
        {
            temp[k++] = data[j++];
            cnt += mid - i + 1;
        }
    }

    while(i <= mid)
    {
        temp[k++] = data[i++];
    }
    while(j <= right)
    {
        temp[k++] = data[j++];
    }

    for(i = 0; i < k; i++)
    {
        data[left+i] = temp[i];
    }
}

个人见解,如有错误,欢迎指正与交流!

获取更多精彩,请关注「seniusen」!

原文地址:https://www.cnblogs.com/seniusen/p/9161682.html

时间: 2024-10-12 07:43:34

今日头条 2018 AI Camp 6 月 2 日在线笔试编程题第二道——两数差的和的相关文章

百度AI人工智能产品体验不好?今日头条进军AI人工智能领域?

关于AI智能生活问题? Python语言是人工智能要用的.还有就是C+和JAVA等一些语言,算法我个人觉得应该是重点.这相当是人的大脑. 2018年12月23号,这一天传来一个非常不好的消息.也就是25号圣诞节那一天,公司发布公告:这一天每个人都要上班,领导来检查.我就想知道那个领导非得圣诞节来啊!好吧认了,毕竟给人打工得遵守别人定的规则. ** AI人工智能广泛性** 回到原题,现在非常多AI智能家具,电器,数码.也有很多盆友已经被深深吸引了!如今"人工智能"已经普遍走进中国大学校园

3月19日在线研讨会预热 | Stages — 研发过程可视化建模和管理平台

Stages 可用于帮助企业定义.管理.发布.控制.优化其研发过程,同时使其研发过程符合CMMI.ASPICE.ISO26262 等标准.Stages 的核心理念是把过程理论和实际项目进行有机结合.Stages聚焦于研发过程的用户体验,允许用户集中访问过程描述信息.项目文档.模板.应用案例或者技术知识库.在Stages中定义好的过程,可以直接用于项目管理或者配置管理系统,如ClearCase.SVN.RTC.JIRA等. 产品介绍 ? 简单灵活的过程建模Stages 提供了非常简单灵活的可视化建

4月9日在线研讨会预热 | Romax — 传动系统设计仿真工具

经纬恒润作为Romax 工具国内业务的代理商,致力于为传动系统.齿轮.轴承行业提供解决方案.英国Romax 公司是一家集软件开发与项目咨询为一体的传动技术公司,其工具主要应用于齿轮箱.轴承和动力传动系统的设计仿真分析,在传动领域享有盛誉,是汽车.工业.风电及轴承应用领域的标准分析软件. 传动系作为机械系统核心部件之一,对传动效率.耐久性和NVH 性能等有较高要求.经纬恒润基于Romax 提供的传动系设计研发方案,将方案设计.详细设计和仿真验证三个环节进行整合. 产品介绍 ? 平台功能变速箱.车桥

4月12日.xyz域名总量10强:西数夺魁 增速放缓

IDC评述网(idcps.com)04月13日报道:根据ntldstats.com发布的最新数据显示,截至2016年4月12日17时,在全球.xyz域名市场上,中国有3家域名商的注册量跻身十强,分别是西部数码.阿里云(万网).新网.其中,西部数码以.xyz域名总量1,127,271个继续称霸全球,远远甩开众多域名商,优势无人能及.接下来,IDC评述网为大家带来具体的数据分析. (图)国内外域名服务商.xyz域名注册总量排行榜 根据上图,截至2016年4月12日17时,在全球.xyz域名注册总量十

5月14日 打印100以内与7有关的数

namespace _5月14日_复习 { class Program { public void sewen() { for (int i = 1; i <= 100; i++) { if (i % 10 == 7 || i / 10 == 7 || i % 7 == 0) { Console.Write(i + "\t"); } } } //public int a;//变量可以定义在外面,但在主函数中要县初始化 static void Main(string[] args)

2018 科大讯飞笔试编程题

第一题:争吵问题 有一个队列,每个人要么朝左边(L表示),要么朝右边(R表示),因为每个人都讨厌其他任何人,只要两人面对面就会发生真吵.真吵结果是胜者留在队列中,败的人移除队中. 如果序列中有多对争吵,可以任选一对,胜者留在队中,败者出局,求最后队列最少人数是多少. 例子: LRLRLRR 输出:3 LRR. 思路:这道题找到方法了就很简单,依据题意 如果要真吵就只有  RL (两人对面)这种情况  ,其他  LL   RR   LR 都不会发生真吵. 所以我们只要从左边向右找到第一个R,从右边

2015年9月百度前端在线笔试

我的空间www.cumt.top 刚结束百度前端的在线笔试,感觉被虐了 一道系统题,三道编程题,js写的云里雾里 第一道系统题:写出资源管理器的数据结构,和功能接口 想了好一会儿,决定数据结构用json来写 { "root":[{ "name":"aaa", "hasChildren":1, "children":[{ "name":"asf"; "hasC

2016年10月15日 算法竞赛刷题所得

1.开灯问题 a[i] = !a[i] 赋相反的值: 注意输入输出: 2.错位输入 常量字符串的使用getchar 3.回文词 常量字符串申明 isalpha()函数的使用 运用两个变量决定字符串在二维字符数组中的位置 4猜数字游戏提示 表示无限循环与while(1)相似,需要在中途通过break跳出 5.最小字典序问题 运用循环比较最优字典序

2020年3月14日 BAPC2019 比赛补题报告

Inquiry I 题意: 给定一个数列, 求公式的最大值 思路: 可以维护一个前缀和 与 后缀和, 扫描一次数组, 即可求出最大值 代码: n = int(input()) lst = [*map(int, input().split())] # left 记录数组元素平方的前缀和, right 记录数组元素后缀和 left, right = 0, sum(lst) res = 0 for i in lst: left += i**2 right -= i res = max(res, lef