Excellent Engineer (线段树)(个人详解)

这道题当时做的时候根本没有想到用线段树,其实这道题用的完全是线段树的基本用法,写起来也比较简单。

但是对我们这种新手来讲真的不好想。

花店时间好好解释下吧,也便于以后复习看看

You are working for an agency that selects the best software engineers from Belgium, the Netherlands and Luxembourg for employment at various international companies. Given the very large number of excellent software engineers these countries have to offer, the agency has charged you to develop a tool that quickly selects the best candidates available from the agency's files.

Before a software engineer is included in the agency's files, he has to undergo extensive testing. Based on these tests, all software engineers are ranked on three essential skills: communication skills, programming skills, and algorithmic knowledge. The software engineer with rank one in the category algorithmic knowledge is the best algorithmic expert in the files, with rank two the second best, etcetera.

For potential customers, your tool needs to process the agency's files and produce a shortlist of the potentially most interesting candidates. A software engineer is a potential candidate that is to be put on this shortlist if there is no other software engineer in the files that scores better on all three skills. That is, an engineer is to be put on the shortlist if there is no other software engineer that has better communication skills, better programming skills, and more algorithmic knowledge.

Input Format

On the first line one positive number: the number of test cases, at most 100100. After that per test case:

  • one line with a single integer nn (1 \le n \le 10^5)(1n105): the number of software engineers in the agency's files.
  • nn lines, each with three space-separated integers r_1r1r_2r2 and r_3r3 (1 \le r_1, r_2, r_3 \le n)(1r1,r2,r3n): the rank of each software engineer from the files with respect to his communication skills, programming skills and algorithmic knowledge, respectively.

For each skill ss and each rank xx between 11 and nn, there is exactly one engineer with r_s = xrs=x.

Output Format

Per test case:

扫描二维码关注公众号,回复: 2657884 查看本文章
  • one line with a single integer: the number of candidates on the shortlist.

样例输入

3
3
2 3 2
3 2 3
1 1 1
3
1 2 3
2 3 1
3 1 2
10
1 7 10
3 9 7
2 2 9
5 10 8
4 3 5
7 5 2
6 1 3
9 6 6
8 4 4
10 8 1

样例输出

1
3
7


题目来源

Benelux Algorithm Programming Contest 2014 Final


题目意思很好懂,找出一些人,对于这些人,他们的三项能力值没有人比他同时都高,输出的是人数。


但是这么想都是一个O(n²)的算法,想优化到O(n*log(n)) 感觉还是要点脑筋。

我们将三个能力分别记为a,b,c

我们首先按第一个能力排一下序,然后模拟一下,发现排序是可行的, 然后传统的暴力就是找遍历前面选中的人,看他们是否有任意一个人比当前判断的人三项值都小(实际上只用判断两个值b,c),如果存在一个人,那么就说明选中的人里面有人比这个人强,所以不能将这个人选进去。。。。  那么这是n²的算法。

//如果基础不好的,建议写一下暴力,多测几组数据,然后再来写线段树


其实遍历前面选中的人是可以进行优化的。

第一,只需要判断b和c的值

第二,也是最重要的,我们记当前判断的人的三个值为 x,y,z,我们只需要在选中的人中 先选出 b值在 1-y 之间的人,判断是不是这些人的c值都比 z小,  其实这还不是最终的优化,  最后我们是找出 b值在1-y之间 的人 所对应的最小 c值,  只要z小于这个最小值c,就说明他可以入围,因为虽然别人的b值比他小,但是c值比不上这个判断的人,反之不能入围。

还是举个例子吧,感觉还是不是那么好懂。

1 4 7

2 5 6

3 6 8

4 7 1

第一个人  1 4 7    一开始直接被选中

那么第二个人  b值为 5,我要去找之前选中的人中  b值在1-5的人,找出他们的最小c值,这里是7(第一个人的),6是小于7的,所以他可以入围,因为他的c值为6,是非常小的了,非常靠前了。

第三个人  找之前b值1-6,最小c值为6,   8是大于6的,所以他不能进去

第四个人和第二个人一样,可以入围。


总之,我们就是要判断之前选中的人里面有没有一个人比这个待定的人更优秀,所谓的优秀就是三项值都要强,而第一项值是排了序的了,所以是递增的,不用考虑, 第二项值之所以要在1-b中找  而不是向 b到+∞找 是因为1-b代表的是可能更优秀的人群,而我们主要的任务就是找有没有一个人比他更优秀,如果有,他就不能进去。    然后c值,c值决定这个人能否入围,

c值如果比之前的c值要小,就说明他也非常优秀(有一技之长),就可以入围,但是这个人的c值一旦大于入围的人最小的c值

,就说明入围的人中,不仅有人b值比他小,c值也比他小,而a值就更不用说了,排序了的,所以不能行。

这样,我们就简化了判断,而且可以和线段树扯上关系。


下面将如何应用到线段树上。

个人觉得构造线段树要思考两个问题,①拿什么做范围,②值存什么

根据前面的过程,我们取b值做范围,值存最小c值。

所以,每次新加入一个人,我们就在 position = b 的为值插入 c(b,c都是这个人的值),然后更新线段树

判断就是查询1-b的最小c值,就是一个普通的query函数就解决了,所以,写起来是非常简单的。


然后,每个人写线段树的风格都非常不同,所以主要还是看思路,代码真的非常简单了

欢迎留言

#include <iostream>
#include<cstdio>
#define inf 0x3f3f3f3f
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 1e5+5;

#define mid (l+r)>>1
#define lef rt<<1
#define rig rt<<1|1

struct spe
{
    int a,b,c;
};
int L,R;    //查询和更改函数的大界限
spe p[maxn];
struct segtree
{
    int mi;     //范围内最小的 第三能力值
};
segtree tree[maxn<<3];

bool cmp( spe t1,spe t2 )   //按照第一种能力排序
{
    return t1.a < t2.a;
}
void change( int rt )
{
    tree[rt].mi = min( tree[lef].mi,tree[rig].mi );
}
int query( int l,int r,int rt )     //询问范围内最小的 第三能力值 c
{
    if( l >= L && r <= R )
        return tree[rt].mi;
    int m = mid;
    int ans = inf;
    if( m >= L )
        ans = min( ans,query(l,m,lef) );
    if( m < R )
        ans = min( ans,query(m+1,r,rig) );
    return ans;
}

void update( int l,int r,int rt,int pos,int val )   //本题的更新是点更新
{
    if( l == r )
    {
        tree[rt].mi = val;
        return;
    }
    int m = mid;
    if( m >= pos )
        update(l,m,lef,pos,val);
    else update(m+1,r,rig,pos,val);
    change(rt);
}
int main()
{
    int T,n;
    scanf("%d",&T);
    while( T-- )
    {
        memset(tree,inf,sizeof tree);
        scanf("%d",&n);
        for( int i = 1 ; i <= n ; i++ )
            scanf("%d %d %d",&p[i].a,&p[i].b,&p[i].c);
        sort(p+1,p+1+n,cmp);
        int ans = 0;
        for( int i = 1 ; i <= n ; i++ )
        {
            L = 1,R = p[i].b;     //线段树查询的范围
            int t = query(1,n,1);
            if( p[i].c < t )
            {
                ans++;
                update(1,n,1,p[i].b,p[i].c);
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39627843/article/details/81022219