美文网首页线段树线段树
MM编程俱乐部 (线段树)

MM编程俱乐部 (线段树)

作者: miaozasnone | 来源:发表于2019-07-31 14:30 被阅读0次

MM编程俱乐部

ACM is popular in HDU. Many girls want to learn more about programming skills in ACM. As one of assistances of Lcy, Yifenfei is now busy preparing a new club called “MM Programming Club”. Of Course, He will be the leader of the club, and teach these girls patiently. After the news posted around the campus, as many as N girls are determined to take part in the club. However, the numbers of members are limited; Yifenfei will only select K of them. It is quite a difficult problem. Here is a list of all information about N girls. Each of them has intelligence value and prettiness value. He also wants these K members such that the difference of intelligence between any two of them must not be greater than MAXK (If K = 1, the difference is zero). Now he wants to maximize the Sum of these K girls’ prettiness value.

Input

Many test case, please process to end of file. Each test first contains three integers N(1 <= N <= 200), K(1 <= K <= N), MAXK(1 <= MAXK <= 500). Then N lines follow. Each line contains two integers S, T (1 <= S, T <= 500). S represents the intelligence value, and T represents the prettiness value.

Output

If he can’t succeed in selecting K girls, print “-1”. Otherwise, Print the maximum the Sum of these K girls’ prettiness value.

Sample Input

2 1 0
1 2
2 3
2 2 0
1 2
2 3
2 2 1
1 2
2 3

Sample Output

3
-1
5

题目的大概意思是在n个女孩中取k个女孩在满足她们之间的智商差小于一个MAXK的情况下,使她们的颜值和最大。
前面第一眼看到就是DFS+剪枝,时间复杂度是O(n!)会TLE。
另一种是采取贪心的思想,将女孩按智商排序,然后遍历所有智商差为k的区间,区间按颜值后取前k个和sum,然后取所有区间的sum中最大的一个。这种方法的时间复杂度是O(n^2*logn)。

//由于本人比较懒
//这里贴出的是杨同学的代码
#include<iostream>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
#define mst(a,b) memset(a,b,sizeof(a))
using namespace std;
const int maxn=10000+5;
struct node{
    int w,v;
}stu[maxn];
bool cmp1(node x,node y)
{
    return x.w<y.w;
}
bool cmp2(node x,node y)
{
    return x.v>y.v;
}
int n,k,limit,res,ans;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    while(cin>>n>>k>>limit)
    {
        res=-1;
        for(int i=1;i<=n;i++)
        cin>>stu[i].w>>stu[i].v;
        sort(stu+1,stu+n+1,cmp1);
        for(int i=1;i<=n;i++)
        {
            ans=stu[i].v;
            int w=stu[i].w;
            vector<node> tmp;
            for(int j=i+1;stu[j].w-w<=limit&&j<=n;j++)
                tmp.push_back(stu[j]);
            if(tmp.size()<k-1) continue;
            sort(tmp.begin(),tmp.end(),cmp2);
            for(int m=0;m<k-1;m++)
                ans+=tmp[m].v;
            res=max(res,ans);
        }
        cout<<res<<endl;
    }
    return 0;
}

第三种方法是用线段树维护,建一颗(1,T)的的线段树,维护区间内的女孩个数与颜值和,先将所有女孩按智商排序,然后遍历更新到线段树中去,如果树中人数等于k或者是智商差值大于MAXK,一个个减去最左边的女孩直到满足要求,取所有当树中人数等于k的颜值和,也就是MAX{tree[1].sum}。首先一次排序的时间复杂度是nlogn,后面的遍历每次树的更新操作是logT,时间复杂度是nlogT,由于T>n所以总的时间复杂度就是nlogT。

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
struct node{
    int s,t;
    bool operator < (const node & e) const {
            return s<e.s;
        }
};
struct seg{
    int l,r,mid;
    int s;
    int num;
};
seg tree[505<<2];
node n_[205];
int ri;
void build(int k,int l,int r){
    tree[k].l=l;
    tree[k].r=r;
    tree[k].mid=(l+r)>>1;
    tree[k].s=tree[k].num=0;
    if(l==r)return;
    build(k<<1,l,tree[k].mid);
    build(k<<1|1,tree[k].mid+1,r);
}
void pushup(int k){
    tree[k].s=tree[k<<1].s+tree[k<<1|1].s;
}
void add(int k,int x){
    tree[k].num++;
    if(tree[k].l==tree[k].r){
        tree[k].s+=x;
        return;
    }
    if(x<=tree[k].mid)add(k<<1,x);
    else add(k<<1|1,x);
    pushup(k);
}
void sub(int k,int x){
    tree[k].num--;
    if(tree[k].l==tree[k].r){
        tree[k].s-=x;
        return;
    }
    if(x<=tree[k].mid)sub(k<<1,x);
    else sub(k<<1|1,x);
    pushup(k);
}
int sum(int k,int x){
   
    if(!x)return 0;
    if(tree[k].l==tree[k].r){
        return x*tree[k].l;
    }
    if(x<=tree[k<<1|1].num)return sum(k<<1|1,x);
    else return sum(k<<1,x-tree[k<<1|1].num)+tree[k<<1|1].s;
}
int main(){
    int n,k,d;
    while (scanf("%d%d%d",&n,&k,&d)!=EOF){
       for(int i=1;i<=n;i++){
          scanf("%d%d",&n_[i].s,&n_[i].t);
       }
       sort(n_+1,n_+1+n);
        ri=1;
        build(1,1,500);
        int res=-1;
        for(int i=1;i<=n;i++){
            add(1,n_[i].t);
            while (ri<=i&&n_[i].s-n_[ri].s>d){
                sub(1,n_[ri].t);
                ri++;
            }
            if(tree[1].num>=k){
                res=max(res,sum(1,k));
            }
        }
        printf("%d\n",res);
    }
}

相关文章

  • MM编程俱乐部 (线段树)

    MM编程俱乐部 ACM is popular in HDU. Many girls want to learn m...

  • 【译】Swift算法俱乐部-线段树

    本文是对 Swift Algorithm Club 翻译的一篇文章。Swift Algorithm Club是 r...

  • 算法模板(七) 线段树

    线段树单点操作 线段树区间操作

  • 数据结构-线段树

    实现一个线段树 下面实现的线段树,有三个功能: 把数组构建成一颗线段树 线段树的修改 线段树的查询 303号问题 ...

  • 线段树系列之——区间更新

    第三篇线段树了——重点不在于解决题目,通过题目理解线段树才是重点 前面写了一篇关于线段树的单点更新,线段树的单点更...

  • 线段树模板

    线段树 线段树基本概念 概述 线段树,类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组)...

  • 线段树专题整理

    待更新 线段树讲解(未读)线段树模板(未读) 模板 求区间总和 A - 敌兵布阵 HDU - 1166 题意 线段...

  • 线段树 02 构建线段树

    构建线段树 线段树的每个节点除了天然的对应一段长度外,不一定赋予其上的意义就是区间元素的和,所以两个节点向上汇聚成...

  • 线段树(区间树)

    线段树:线段树是一种二叉树,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。线段树适用于不变...

  • 线段树

    专题 B线段树求逆序对 C[] D 区间不同值求和

网友评论

    本文标题:MM编程俱乐部 (线段树)

    本文链接:https://www.haomeiwen.com/subject/vhgzrctx.html