离开学校出社会工作已久,甚是怀念大学时代ACM的时光,偶遇组织于10.24举行编程挑战赛,遂前去试了一下,很幸运地也拿了点小奖,后于此简单分享。

10.24挑战赛题解

题目描述:

某客户来到我司,购买了N份保单,每份保单都有一个起始时间和终止时间。 为了简化计算,这两个时间使用两个整数来表示,取值范围为0到1,000,000,000。 例如,其中一份保单的起始时间t=3且终止时间t=9,那么这份保单的有效区间则覆盖了6个单位时间。 可是,由于个人原因,该客户想退掉其中1份保单,求剩余保单能覆盖的最大单位时间总量是多少?

输入输出格式:

我们会在当前目录给定一个输入文件 “input.txt”

  • 该文件第一行是一个整数N(0<N<=100,000),表示接下来总共有N行。 接下来的N行,每行表示一份保单在0到1,000,000,000范围内的起始以及终止时间(以空格分割)。 所有的终止时间都是不同的。不同的保单能覆盖的时间也可能会重叠。

请给出一个输出文件 “output.txt”

  • 该文件中请输出一个数,表示客户退保1份保单仍能得到覆盖的最大单位时间总量。

输入输出举例:

输入文件(input.txt):
3
6 9
1 5
3 7

输出文件(output.txt):
7

题目分析

  • 最多会有10万个区间,循环、排序起来的耗时还在能接受的范围
  • 区间的两个端点数值,符合32位int型范围,不会溢出
  • 题目未涉及到大数的运算,应该主要是考算法实现,只要理解题目,加以适当算法嵌套,解题应该不难。

暴破原理读懂题目:
假设分配10亿个桶,分别对应10亿段小区间(数组可实现),每个保单读入时便在对应桶里加一个球(+1操作),录完之后,再找到最无用的保单剔除,最终根据有多少个桶有球,就是题目的答案。

解题思路

这道题其实是一个动态规划的题,因为每次保单的录入,都会影响最优选择。

最无用的保单怎么找,其实分两种情况:

  • 每个保单都不重叠,直接找跨越区间最短的即可
  • 保单交叉重叠。
    • 这里我选择徒手画多个圆形,任意交叉来帮助理解(因为本题是多个圆形重叠的简化版)。
    • 最终发现,只需要计算出每个圆自己独立拥有的区域大小(本题是区间大小)即可,算出来后再筛选最小值,就得到最无用的保单,剔除即可。

我采用的算法逻辑

  • 1.依次读取N个保单,并计算保存每个保单的区间长度
  • 2.按保单起点、终点递增方式,进行二级快速排序,便于后面做大区段拆分。
  • 3.划分成大区间,每个大区间都不重合(多个重叠的小区间则取最小&最大值作为大区间),这样只要最后进行累加计算即可。
  • 4.重叠的小区间逐个计算权重(独立拥有的区域大小),权重最小的为最无用的保单。
  • 5.权重计算优化,当重叠区域出现权重为0时(类似同心圆情况,即被包含了),直接返回此项为最无用保单,减去此权重就是答案了。
  • 6.本来还想考虑如何省略快排所占用(复杂度NLogN)的时间,想了一些逻辑没有深入,总感觉如果区间段很多的话还是划分大区间会好一点,所以没有再做优化。

代码实现

#include <stdio.h>
#include <stdlib.h>

struct node
{
    int start;
    int end;
    int range;
    int weight;
}s[100000];

struct range {
    int min_pos;
    int max_pos;
    struct node data;
};

int cmp (const void * a, const void * b)
{
    struct node* c = (struct node*)a;
    struct node* d = (struct node*)b;
    if(c->start != d->start) {
        return c->start - d->start;
    } else {
        return c->end - d->end;
    }
}

int calWeight(int cur_pos, int start, int end, int length) {
    int overlap_pre=0, overlap_next=0, universe=0;
    int i, j, tmp;

    i = cur_pos-1;
    while(i>=start && s[i].end>s[cur_pos].start) {
        tmp = s[i].end - s[cur_pos].start;
        if(overlap_pre < tmp) {
            overlap_pre = tmp;
        }
        i--;
    }

    j = cur_pos+1;
    while(j<end && s[j].start < s[cur_pos].end) {
        if(s[j].end < s[cur_pos].end) {
            return -1;
        }
        tmp = s[cur_pos].end - s[j].start;
        if(overlap_next < tmp) {
            overlap_next = tmp;
        }
        j++;
    }

    universe = s[cur_pos].range - overlap_pre - overlap_next;

    return (universe<0 ? 0 : universe);
}

struct range distinctRange(int pos, int N) {
    int min_pos, max_pos, i, length, useless_weight=-9999;
    struct range res;
    min_pos = max_pos = pos; i = pos + 1;
    while(i<N && s[i].start < s[max_pos].end) {
        if(s[i].start < s[min_pos].start) {
            min_pos = i;
        }
        if(s[i].end > s[max_pos].end) {
            max_pos = i;
        }
        i++;
    }
    length = s[max_pos].end - s[min_pos].start;
    res.min_pos = pos; res.max_pos = i - 1;
    if(i == pos+1) {
        s[pos].weight = s[pos].range;
        useless_weight = s[pos].weight;
    } else {
        for(int j=pos; j<i; j++) {
            s[j].weight = calWeight(j, pos, i, length);
            if(s[j].weight == -1) {
                res.data.start = s[min_pos].start;
                res.data.end = s[max_pos].end;
                res.data.range = length;
                res.data.weight = 0;
                return res;
            }
            if(useless_weight == -9999 || s[j].weight<useless_weight) {
                useless_weight = s[j].weight;
            }
        }
    }
    
    res.data.start = s[min_pos].start;
    res.data.end = s[max_pos].end;
    res.data.range = length;
    res.data.weight = useless_weight;
    return res;
} 

int main()
{
    int N, i, total=0;
    FILE * fp;
    struct range g_range = {0}, g_min_range={0};
    
    fp = fopen ("input.txt", "r+");
    rewind(fp);
    fscanf(fp, "%d", &N);
    
    for(i=0; i<N; i++) {
        fscanf(fp, "%d %d", &s[i].start, &s[i].end);
        s[i].range = s[i].end - s[i].start;
    }
    
    qsort(s, N, sizeof(s[0]), cmp);

    i=0;
    while(i < N) {
        g_range = distinctRange(i, N);
        i = g_range.max_pos;
        total += g_range.data.range;
        if(g_min_range.max_pos==0 || g_range.data.weight < g_min_range.data.weight) {
            g_min_range = g_range;
        }
        i++;
    }

    total -= g_min_range.data.weight;

    FILE *fpt;
    fpt = fopen("output.txt","w");
    fprintf(fpt,"%d",total);
    fclose(fpt);

    fclose(fp);
    return(0);
}