Google笔试:Watson and Intervals

原创 2016年08月30日 13:04:10

问题

这是今年(2016)google校招的笔试题(Round B的C题),难度比acm的低,但是也不简单。
原题链接:http://code.google.com/codejam/contest/5254487/dashboard#s=p2

问题意思是:给你一堆区间,让你选择一个来去掉,使得剩下的区间覆盖到的整数点最少。

比如这些区间是{[2, 5], [3, 5], [4, 7]},
1)去掉[2, 5]的话,剩下的区间覆盖的整数点为3,4,5,6,7,有5个;
2)去掉[3, 5]的话,剩下的区间覆盖的整数点为2,3,4,5,6,7,有7个;
3)去掉[4, 7]的话,剩下的区间覆盖的整数点为2,3,4,5,有4个。
所以很明显应该去掉最后一个区间,使得剩下的区间覆盖到的整数点最少。

思路

我的想法是,先算出所有区间覆盖到的整数点数量,这个很简单,参考上一篇博客:区间覆盖与合并

然后,计算每个区间对于整体的独立贡献,找出最大的独立贡献,删掉相应的区间,剩下的自然最少了!

维护两个值:Tail(前面所有区间的右端点的最大值),secondTail(前面所有区间的端点中的第二大的值)。
(secondTail, Tail]这段区间表示还没被其它区间覆盖到的!所以想要计算每个区间对于整体的独立贡献的话,用这个区间来计算就好了。

举个例子,[1, 10], [2, 4], [7, 15],Tail和secondTail的初始值为1-1=0,则:
第一步:[1, 10],Tail变成10,secondTail变成1-1=0(注意(secondTail, Tail]是一个左开右闭区间)。

第二步:[2, 4],此时我们发现2比secondTail大,那么(secondTail, 2)这段区间就是第一个区间的独立贡献了。因为这段区间,即[1, 1]没有被任何其它区间覆盖到。
然后考虑Tail和secondTail的更新,4比10小,所以此时Tail还是10,secondTail呢?应该要变成4!因为[1, 1]已经计算过了,而[2, 4]被第二个区间占用了,剩下的就是(4, 10]了!所以secondTail应该是4。

第三步,[7, 15],我们发现7比secondTail大,所以(4, 7)这段区间也是第一个区间的独立贡献,没有别的区间覆盖到,给它加上。
再考虑Tail和secondTail的更新,15比10大,所以此时Tail应该是15。secondTail呢?应该要变成10!因为[1, 1]已经计算过了,而[2, 4]被第二个区间占用了,[5, 6]也被计算过了,[7, 10]被第三个区间占用了,剩下的就是(10, 15]了!所以secondTail应该是10,并且此时开始,计算独立贡献的应该是加到第三个区间上,而不是第一个区间了!

最后的结果,三个区间的独立贡献值分别是:3(点1,5,6),0,5(点11,12,13,14,15)。

上面的举例所说的操作是挺明显的,不过具体的规则是怎样的呢?

更新独立贡献值

由于我们已经知道,目前还没被其它区间覆盖的区间是(secondTail,Tail],那么对于当前这个区间[F, S],怎么样才会更新独立贡献值呢?

如果F比secondTail小会怎样?那就是覆盖了(secondTail, S]这一段,并不会有没有覆盖到的点可以来更新!

所以应该是F大于secondTail才会有更新的!(看上面的例子)

我们现在知道secondTail < F,F <= S,而secondTail <= Tail,四者的大小关系不是唯一的,所以分类讨论一下:

大小关系 增加的独立贡献 secondTail的新值 Tail的新值
secondTail < F <= S <= Tail (secondTail, F) S Tail
secondTail < F <= Tail < S (secondTail, F) Tail S
secondTail <= Tail < F <= S (secondTail, Tail] F S
总结规律 (secondTail, min(Tail, F-1)] 四者中的第二大者 四者中的最大者

更新secondTail和Tail的值

上面讨论了有更新时候secondTail和Tail的更新规律,下面就看一下没有更新时,两者的更新规律。

此时没有更新,那么必然有F <= secondTail,所以四者的大小关系可能是:

大小关系 secondTail的新值 Tail的新值
F <= S <= secondTail <= Tail secondTail Tail
F <= secondTail < S <= Tail S Tail
F <= secondTail <= Tail < S Tail S
总结规律 四者中的第二大者 四者中的最大者

上面这两张表的更新规则是按照(secondTail, Tail]的意义来决定的,也就是要让(secondTail, Tail]这段区间保持没被当前的任何区间覆盖到!!!

再举个例子吧,就上表的第二行, F <= secondTail < S <= Tail,很明显(secondTail, S]这段区间已经被当前区间[F, S]覆盖到了,所以secondTail应该被更新为S,再验证一下(S, Tail]这段区间是不是还没被其它区间覆盖到?是滴!

细节

最后再说两个点,第一,可以发现,上面两个表中,secondTail和Tail的更新规律都是一样的,所以可以合并。

第二,如何轻松找出四个数字中最大和第二大的数字呢?
排序后选?sb了吧。
这里只要利用好已知的大小关系,是可以很优雅写出来的。
我们已知,secondTail <= Tail,并且F <= S。
那么最大的数字只可能在S和Tail中产生,对不对?

那么第二大呢?
如果S >= Tail,那么第二大的数字只能在F和Tail之间产生(因为肯定不可能是secondTail,Tail比secondTail大)。
否则S < Tail,那么第二大的数字只能在S和secondTail之间产生(因为肯定不可能是F,S还比F大啊)。

可以这样写:

if (S >= Tail)
    secondTail = max(Tail, F);
else
    secondTail = max(secondTail, S);
Tail = max(Tail, S);

代码

代码其实很好写了,因为规律都推断出来了!!!

LL cover(RangeList& intervals) {
    // 注意初始值
    LL Tail = intervals[0].first - 1, secondTail = Tail, TailIndex = -1;
    vector<LL> maxPoints(intervals.size());

    for (int i = 0; i < intervals.size(); ++i) {
        // 更新独立贡献值
        if (i > 0 && intervals[i].first > secondTail) {
            maxPoints[TailIndex] += min(Tail, intervals[i].first - 1) - (secondTail + 1) + 1;
        }

        // 更新secondTail值
        if (Tail > intervals[i].second)
            secondTail = max(secondTail, intervals[i].second);
        else
            secondTail = max(Tail, intervals[i].first-1);

        // 更新Tail值
        if (intervals[i].second > Tail) {
            Tail = intervals[i].second;
            TailIndex = i;
        }
    }

    // 别忘了最后还得再算一次!
    maxPoints[TailIndex] += Tail - (secondTail+1) + 1;

    // 找出最大的独立贡献值
    LL maxPoint = 0;
    for (int i = 0; i < maxPoints.size(); ++i) {
        maxPoint = max(maxPoint, maxPoints[i]);
    }
    return maxPoint;
}

整道题的代码是:

#include <iostream>
#include <stdio.h>
#include <string>
#include <vector>
#include <algorithm>
#include <map>
#include <set>
using namespace std;

typedef long long LL;
typedef vector<pair<LL, LL> > RangeList;


LL coverAll(RangeList& intervals) {
    LL left = intervals[0].first, right = intervals[0].second;
    LL area = 0;
    for (int i = 1; i < intervals.size(); ++i) {
        // 前面自成一个区间,那么就此分开
        if (intervals[i].first > right) {
            area += right - left + 1;
            left = intervals[i].first;
            right = intervals[i].second;
        } else if (intervals[i].second > right) {
            right = intervals[i].second;
        }
    }
    area += right - left + 1;

    return area;
}


LL cover(RangeList& intervals) {
    // 注意初始值
    LL Tail = intervals[0].first - 1, secondTail = Tail, TailIndex = -1;
    vector<LL> maxPoints(intervals.size());

    for (int i = 0; i < intervals.size(); ++i) {
        // 更新独立贡献值
        if (i > 0 && intervals[i].first > secondTail) {
            maxPoints[TailIndex] += min(Tail, intervals[i].first - 1) - (secondTail + 1) + 1;
        }

        // 更新secondTail值
        if (Tail > intervals[i].second)
            secondTail = max(secondTail, intervals[i].second);
        else
            secondTail = max(Tail, intervals[i].first-1);

        // 更新Tail值
        if (intervals[i].second > Tail) {
            Tail = intervals[i].second;
            TailIndex = i;
        }
    }

    // 别忘了最后还得再算一次!
    maxPoints[TailIndex] += Tail - (secondTail+1) + 1;

    // 找出最大的独立贡献值
    LL maxPoint = 0;
    for (int i = 0; i < maxPoints.size(); ++i) {
        maxPoint = max(maxPoint, maxPoints[i]);
    }
    return maxPoint;
}


int main() {
    int t;
    cin >> t;
    for (int time = 1; time <= t; ++time) {
        LL N, L1, R1, A, B, C1, C2, M;
        cin >> N >> L1 >> R1 >> A >> B >> C1 >> C2 >> M;
        RangeList intervals;
        for (int i = 0, x = L1, y = R1; i < N; ++i) {
            intervals.push_back(make_pair(min(x, y), max(x, y)));
            LL x_last = x, y_last = y;
            x = (A * x_last + B * y_last + C1) % M;
            y = (A * y_last + B * x_last + C2) % M;
        }
        sort(intervals.begin(), intervals.end());
        cout << "Case #" << time << ": " << coverAll(intervals) - cover(intervals) << endl;
    }

    return 0;
}

Watson Explorer 入门(4):内容分析工作室(Studio)使用方法

内容分析工作室是一个开发环境,用来构建和测试应用领域的的文本分析引擎。这个环境,消除了对自然语言处理或UIMA的底层技术需要专业知识。通过使用内容分析工作室,您可以开发文本分析引擎,而无需编写任何代码...
  • quicmous
  • quicmous
  • 2017年04月28日 15:32
  • 468

IBM的语音识别(IBM speech to text 语言转换成文字)

参考资料: https://www.ibm.com/watson/developercloud/speech-to-text.html
  • nicolelili1
  • nicolelili1
  • 2017年04月01日 18:35
  • 11870

Google 2015 校招第四轮在线技术笔试 解题报告

题目在这里:https://code.google.com/codejam/contest/6214486/dashboard zhe
  • lwfcgz
  • lwfcgz
  • 2014年11月09日 17:28
  • 1523

Merge Intervals -- LeetCode

原题链接: http://oj.leetcode.com/problems/merge-intervals/  这是一道关于interval数组结构的操作,在面试中也是一种比较常见的数据结构。假设这...
  • linhuanmars
  • linhuanmars
  • 2014年03月23日 08:16
  • 11739

[leetcode-56]Merge Intervals(java)

问题描述: Given a collection of intervals, merge all overlapping intervals.For example, Given [1,3],[2...
  • zdavb
  • zdavb
  • 2015年08月03日 10:28
  • 888

Bluemix 之 IBM Watson Conversation 自然对话实践

Bluemix 之 IBM Watson Conversation 自然对话实践前言 初步结识Bluemix是在2015年春节,那时候对于一个外来的IBM-PaaS很是好奇,但是又不敢尝试,担心成...
  • qq_31810357
  • qq_31810357
  • 2017年05月31日 10:58
  • 2763

LeetCode 56:Merge Intervals

Given a collection of intervals, merge all overlapping intervals. For example, Given [1,3],[2,6],[8...
  • sunao2002002
  • sunao2002002
  • 2015年06月02日 21:17
  • 1774

leetCode 56.Merge Intervals (合并区间) 解题思路和方法

Merge Intervals  Given a collection of intervals, merge all overlapping intervals. For example...
  • xygy8860
  • xygy8860
  • 2015年07月15日 14:06
  • 1425

GOOGLE校招的笔试题解析

据说是GOOGLE校招的笔试题: 用二进制来编码字符串“abcdabaa”,需要能够根据编码,解码回原来的字符串,最少需要多长的二进制字符串? A.12 B.14 C.18 D.24 答...
  • coloriy
  • coloriy
  • 2015年08月24日 15:38
  • 435

Watson Explorer 入门(1):非结构化数据相关概念

IBM Watson Explorer 是 IBM 认知技术最核心的一款产品。认知技术的核心是非结构化数据的处理能力。所以先来讨论一下非结构化数据的相关概念。结构化、半结构化、非结构化数据在我们和计算...
  • quicmous
  • quicmous
  • 2017年04月21日 11:30
  • 1307
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Google笔试:Watson and Intervals
举报原因:
原因补充:

(最多只允许输入30个字)