首页 新闻 会员 周边

一道阿里的编程题

0
悬赏园豆:20 [待解决问题]

有1,2,3....n个数组,每个数组包含一系列一维线段的表示,每个数组的元素结构为(point,length)(point>=0 且 length>=1,都为整数),表示从point开始长为length的线段,现将n个数组中的线段合并,其中需要考虑数组的优先级:1>2>....>n,高优先级的数组的线段将覆盖并切分重叠的低优先级数组线段。求合并后的数组?

示例:

1数组:(0,2),(6,9)

2数组:(1,3),(7,10)

合并后:(0,2),(2,2),(6,9),(15,2)

输入:

n(数组个数)

依次输入n个数组。

输出:

合并后的数组。

luoyin500的主页 luoyin500 | 初学一级 | 园豆:89
提问于:2018-04-02 11:21

要求什么语言?

写代码的相声演员 6年前
< >
分享
所有回答(3)
0

如果题目的描述和示例出现了冲突或者BUG,应该怎么回答系列。

爱编程的大叔 | 园豆:30839 (高人七级) | 2018-04-02 12:48

其实题目的描述和示例是没问题的。

支持(0) 反对(0) Shendu.CC | 园豆:2138 (老鸟四级) | 2018-04-02 17:25

@Shendu.cc: 谢谢,确实是我看错了。

支持(0) 反对(0) 爱编程的大叔 | 园豆:30839 (高人七级) | 2018-04-02 17:33
0

测试结果:

 

我的方法是,n个数组,从第n个开始,从后往前一个一个合并。

使用set ,方便插入和自动排序已经二分查找,插入一个节点会自动排序,插入的效率O(log n)

set中存放的是最终结果,整体来看时间效率是O(n*m*logn*2) m代表平均每个数组的长度

在插入set的时候,要注意区间的交合,所以条件挺多的。

还没有测大量数据,可能会有bug

#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <map>
#include <algorithm>
#include <set>

using namespace std;
struct Node
{
    int point;
    int length;
    int end;
    
    bool operator <(const Node &r) const
    {
        if(point == r.point)
            return end<r.end;
        else
            return point<r.point;
    }
    
    Node(){}
    Node(int point,int end)
    {
        this->point=point;
        this->end=end;
        this->length = end-point+1;
    }
}a[105][105],res[1005];
set<Node> ans;
bool sort(Node a,Node b)
{
    if(a.point==b.point)
        return a.end<b.end;
    else
        return a.point<b.point;
  
}
int b[105];
int n;
int main()
{
    printf("请输入数组的个数\n");
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        printf("请输入第%d个数组的个数\n",i+1);
        scanf("%d",&b[i]);
        printf("请依次输入point,length\n");
        for(int j=0;j<b[i];j++)
        {
            scanf("%d%d",&a[i][j].point,&a[i][j].length);
            a[i][j].end=a[i][j].point+a[i][j].length-1;
        }
    }
    for(int i=n-1;i>=0;i--)
    {
       
        for(int j=0;j<b[i];j++)
        {
            if(i==n-1)
            {
                ans.insert(Node(a[i][j].point,a[i][j].end));
                continue;
            }
            //返回集合中第一个大于等于当前段的元素,注意集合中是没有相交的段
            set<Node>::iterator  x = ans.lower_bound(a[i][j]);
            //如果当前端比集合中的都小
            if(x==ans.begin())
            {
                Node Max = *x;//第一个大于等于当前段的元素
                if(a[i][j].end>=Max.end)
                {
                    ans.erase(Max);
                    ans.insert(Node(a[i][j].point,a[i][j].end));
                }
                else if(a[i][j].end<Max.end&&a[i][j].end<=Max.point)
                {
                    ans.erase(Max);
                    ans.insert(Node(a[i][j].point,a[i][j].end));
                    ans.insert(Node(a[i][j].end+1,Max.end));
                }
                else if(a[i][j].end<Max.point)
                {
                    ans.insert(Node(a[i][j].point,a[i][j].end));
                }
                continue;
            }
            //如果当前段比集合中的都大
            else if(x==ans.end())
            {
                ans.insert(Node(a[i][j].point,a[i][j].end));
                continue;
            }
            
            Node Max = *x;//第一个大于等于当前段的元素
            x--;
            Node Min = *x;//第一个小于当前段的元素
            
          
            //下面进入插入set操作
         
            if(a[i][j].point<Min.end&&a[i][j].point>=Min.point)
            {
                ans.erase(Min);
                 ans.insert(Node(Min.point,a[i][j].point));
                if(a[i][j].end>=Max.end)
                {
                    ans.erase(Max);
                    ans.insert(Node(a[i][j].point,a[i][j].end));
                    
                }
                else if(a[i][j].end>=Max.point&&a[i][j].end<Max.end)
                {
                    ans.erase(Max);
                    ans.insert(Node(a[i][j].point,a[i][j].end));
                    ans.insert(Node(a[i][j].end+1,Max.end));
                }
                else if(a[i][j].end<Max.point&&a[i][j].end>=Min.end)
                {
                    ans.insert(Node(a[i][j].point,a[i][j].end));
                }
                else if(a[i][j].end<Min.end)
                {
                    ans.insert(Node(a[i][j].point,a[i][j].end));
                    ans.insert(Node(a[i][j].end+1,Min.end));
                }
            }
            else if(a[i][j].point>=Min.end&&a[i][j].point<=Max.end)
            {
                if(a[i][j].end>=Max.end)
                {
                    ans.erase(Max);
                    ans.insert(Node(a[i][j].point,a[i][j].end));
                }
                else if(a[i][j].end<Max.end&&a[i][j].end>=Max.point)
                {
                    ans.erase(Max);
                    ans.insert(Node(a[i][j].point,a[i][j].end));
                    ans.insert(Node(a[i][j].end+1,Max.end));
                }
                else if(a[i][j].end<Max.point)
                {
                    ans.erase(Node(a[i][j].point,a[i][j].end));
                }
            }
            
          
        }
    }
    printf("结果是:\n");
    set<Node>::iterator ite1 = ans.begin();
    set<Node>::iterator ite2 = ans.end();
    for(;ite1!=ite2;ite1++)
    {
        printf("(%d,%d)\n",(*ite1).point,(*ite1).length);
    }
    return 0;
    
    
}
View Code

 

Shendu.CC | 园豆:2138 (老鸟四级) | 2018-04-02 13:50

是否考虑这样一个情况:

如:高优先级a存在(12,10)

b存在(8,6),(17,10)

即a一个线段会重合b多段线段。

支持(0) 反对(0) luoyin500 | 园豆:89 (初学一级) | 2018-04-02 17:49

@luoyin500: 跑出来的结果是

(8,5)

(12,10)

(22,5)

支持(0) 反对(0) Shendu.CC | 园豆:2138 (老鸟四级) | 2018-04-02 17:57

@luoyin500: 你好,经过思考,发现使用set是有问题的。于是我用离散化和暴力重新写了,其实也不是暴力时间效率也不错。你可以出几组测试数据。

//
//  main.cpp
//  test2
//
//  Created by 小康 on 28/03/2018.
//  Copyright © 2018 小康. All rights reserved.
//
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <map>
#include <algorithm>
#include <set>


using namespace std;
struct Node
{
    int point;
    int length;
    int end;
    
    bool operator <(const Node &r) const
    {
        if(point == r.point)
            return end<r.end;
        else
            return point<r.point;
    }
    
    Node(){}
    Node(int point,int end)
    {
        this->point=point;
        this->end=end;
        this->length = end-point+1;
    }
}a[105][105],res[1005];
set<Node> ans;
bool sort(Node a,Node b)
{
    if(a.point==b.point)
        return a.end<b.end;
    else
        return a.point<b.point;
  
}
int b[105];
int n;
map<int,int> m;
int c[10005];
int main()
{

    printf("请输入数组的个数\n");
    scanf("%d",&n);
    int p=1;
    for(int i=0;i<n;i++)
    {
        printf("请输入第%d个数组的个数\n",i+1);
        scanf("%d",&b[i]);
        printf("请依次输入point,length\n");
        for(int j=0;j<b[i];j++)
        {
            scanf("%d%d",&a[i][j].point,&a[i][j].length);
            a[i][j].end=a[i][j].point+a[i][j].length-1;
            c[++p]=a[i][j].point;
            c[++p]=a[i][j].end;
        }
    }
    sort(c+1,c+p+1);
    for(int i=1;i<=p;i++)
    {
        m[c[i]]=i;
    }
    
    int tagl[10005];
    int tagr[10005];
    int tag[10005];
    memset(tagl,0,sizeof(tagl));
    memset(tagr,0,sizeof(tagr));
    memset(tag,0,sizeof(tag));
    int q=1;
    for(int i=n-1;i>=0;i--)
    {
       
        for(int j=0;j<b[i];j++)
        {
            int l=m[a[i][j].point];
            int r=m[a[i][j].end];
          
            for(int k=l;k<r;k++)
            {
                if(tagl[k]!=0)
                {
                    if(tagl[k]>r)
                    {
                        tagl[r]=tagl[k];
                        tagr[tagl[k]]=r;
                        tagl[k]=0;
                     
                        
                    }
                    else
                    {
                        tagr[tagl[k]]=0;
                        tagl[k]=0;
                       
                    }
                }
                
                if(tagr[k]!=0)
                {
                    if(tagr[k]<l)
                    {
                        tagl[tagr[k]]=l;
                        tagr[l]=tagr[k];
                        tagr[k]=0;
                    }
                    else
                    {
                        tagl[tagr[k]]=0;
                        tagr[k]=0;
                    }
                }
            }
            tagl[l]=r;
            tagr[r]=l;
        }
    }
    printf("结果是:\n");
    int pre=0;
    for(int i=1;i<=p;i++)
    {
        if(tagl[i]!=0)
        {
            if(i==pre&&pre!=0)
                printf("(%d,%d)\n",c[i]+1,c[tagl[i]]-c[i]);
            else
                 printf("(%d,%d)\n",c[i],c[tagl[i]]-c[i]+1);
            
            pre=tagl[i];
                
            
        }
    }
            
            
    
    return 0;
    
    
}
View Code
支持(0) 反对(0) Shendu.CC | 园豆:2138 (老鸟四级) | 2018-04-02 19:37
0

中午想了想这道题。

这道题是一个典型的范围问题,这种问题在排时间表时很常见。但是跟排时间表不同的是,这道题要取差集,也就是题中提到的分割。 虽然俗话说的好,没有什么需求不是N个if解决不了的,有的话就加几个for。按自然思维套路就会建一个线段类,然后建个二位数组开始一个一个线段的比较。

但是这道题有个可以偷懒的方法,我们可以用空间换取时间。将最后输出的数组想象成一个1XN的格子,然后传入的线就是一条要涂的颜色。然后倒叙涂抹这条线,随后按照不同颜色输出数组,就是结果。

即使这道题出现小数,让所有线条的所有值×10,让他们放大,如果还有小数继续放大,直到放大到没有小数。当然这个方法一旦出现小数,就会成倍的增大内存开销。需要注意

 

犯懒用C# 写的,没有像上面两个大神用C++,凑合看

using System;
using System.Collections.Generic;

namespace CoreConsoleTest.AliLine
{
    public class Main
    {
        public static void run(string[] args)
        {
            List<List<Line>> lines = new List<List<Line>>
            {
                new List<Line> {
                    new Line { Point = 0, Length = 2 },
                    new Line { Point = 6, Length = 9 }
                },
                new List<Line>{
                    new Line { Point = 1, Length = 3 },
                    new Line { Point = 7, Length = 10 }
                }
            };
           int enlarge = 0;

            //检查放大倍数.如果全都是整数,这个段Foreach可以注释掉
            foreach (var array in lines)
            {
                foreach (var line in array)
                {
                    if (line.Point.ToString().IndexOf('.') > -1)
                    {
                        int len = line.Point.ToString().Length - line.Point.ToString().IndexOf('.') - 1;
                        if (len > enlarge) enlarge = len;
                    }
                    if (line.Length.ToString().IndexOf('.') > -1)
                    {
                        int len = line.Length.ToString().Length - line.Length.ToString().IndexOf('.') - 1;
                        if (len > enlarge) enlarge = len;
                    }

                }
            }
            enlarge = (int)Math.Pow(10, enlarge);

            int color = 0;
            List<int> ColorLine = new List<int>();
            for (int i = lines.Count - 1; i >= 0; i--)
            {
                foreach (var line in lines[i])
                {
                    //当填充线不够长,增加
                    for (int a = ColorLine.Count; a <  line.End * enlarge; a++) ColorLine.Add(-1);

                    for (int p = (int)(line.Point * enlarge); p <  line.End * enlarge; p++) ColorLine[p] = color;

                    color++;

                }
            }

            Console.WriteLine("输出:");
            int point = ColorLine[0], count = 1, start = 0;

            for (int i = 1; i < ColorLine.Count; i++)
            {
                if (point == ColorLine[i])
                {
                    count++;
                }
                else
                {
                    if (point != -1)
                        Console.Write($"({(double)start / enlarge},{(double)count / enlarge}) ");
                    start = i; count = 1; point = ColorLine[i];
                }
            }
            Console.WriteLine($"({(double)start / enlarge},{(double)count / enlarge}) ");
        }

        private class Line
        {
            public double Point { get; set; }

            public double Length { get; set; }

            public double End { get { return Point + Length; } }
        }


    }

}

 

测试小数:

修改测试数据

 List<List<Line>> lines = new List<List<Line>>
            {
                new List<Line> {
                    new Line { Point = 0, Length = 2.12 },
                    new Line { Point = 6, Length = 9 }
                },
                new List<Line>{
                    new Line { Point = 1, Length = 3 },
                    new Line { Point = 7, Length = 10 }
                }
            };

 

写代码的相声演员 | 园豆:517 (小虾三级) | 2018-04-04 15:41

如果测试数据的区间是(1,10000000)这样子,每次染色要循环一千万次,如果n大一点,就跑炸了。

支持(0) 反对(0) Shendu.CC | 园豆:2138 (老鸟四级) | 2018-04-04 16:31

@Shendu.cc: 大神指点的没错,这个方法就是简单粗暴,我上面也说了,这个方法会导致内存暴增。这个是毋庸置疑的。

这个也就是为了得出题目要求结果的一个偷懒的解决方案。

用到生产中这个方法肯定是不可取的,至少会考虑多线程。

关于区间比较的做法我也做了,但是感觉这个简单粗暴的写法更有意思。

支持(0) 反对(0) 写代码的相声演员 | 园豆:517 (小虾三级) | 2018-04-04 16:35

@写代码的相声演员: 你可以做一些小动作,把暴增的内存和暴走的时间都规避掉。不过这也无关紧要了。感兴趣可以研究。

支持(0) 反对(0) Shendu.CC | 园豆:2138 (老鸟四级) | 2018-04-04 16:41

@写代码的相声演员: 测试平台上有OJ限制的。。。。

支持(0) 反对(0) luoyin500 | 园豆:89 (初学一级) | 2018-04-05 16:58

@luoyin500: 敢问OJ是啥?

支持(0) 反对(0) 写代码的相声演员 | 园豆:517 (小虾三级) | 2018-04-08 09:24
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册