zoukankan      html  css  js  c++  java
  • SCOI2007 组队

    传送门

    这道题该怎么做呢……我自己只能想出来O(n^3)的暴力模拟……不过并不行。

    来看一下正解吧……将给定的式子变一下形,得到A*height + B*speed - C <= A*minh+B*mins.

    这样的话,把所有的运动员按照A*height + B*speed - C从小到大排序,这样就使得,如果有i成立,那么对于每个j<i,只要这个j的height和speed都不小于最小要求都是合法的。

    但是不只有这一个限制。由于height还必须大于等于minh,所以对于任意一个运动员,其speed不仅要大于等于mins还要小于等于mins + C/B(这个用上面的式子可以推出)所以我们进行枚举,先枚举mins,之后再枚举minh,对于当前的minh,我们按照sum的顺序从小到大去枚举,如果这个运动员满足其speed >=mins && <= mins + C/B,那么这个运动员当前就是可以取的。这样我们维护了一个右区间,之后,我们再维护左区间,对于每一个height不够的运动员,我们把他从合法区间中踢出。不过有一些本身并没有被算过,所以只有对于s符合要求的那些,我们才会把其删除。这样每次在找完合法区间之后更新答案即可。

    这种算法起到优化的作用在于其利用了单调性。我们来看,在从小到大枚举minh,mins的过程中,我们知道A*minh+B*mins.必然是单调递增的,也就是说,每次向后取一个元素,就会导致更多的人有被选中的机会。而对于已经被删除的人,因为我们枚举的最小高度肯定也是递增的,所以已经被删除的人将来也不可能合法。于是这个算法就用单调性来保证了它的正确性,同时完成了时间复杂度的优化。我们只需要O(2*n^2)的复杂度就可以过了。

    看一下代码。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #define rep(i,a,n) for(ll i = a;i <= n;i++)
    #define per(i,n,a) for(ll i = n;i >= a;i--)
    #define enter putchar('
    ')
    
    using namespace std;
    const int M = 5005;
    typedef long long ll;
    
    int read()
    {
        int ans = 0,op = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
        {
            if(ch == '-') op = -1;
            ch = getchar();
        }
        while(ch >= '0' && ch <= '9')
        {
            ans *= 10;
            ans += ch - '0';
            ch = getchar();
        }
        return ans * op;
    }
    
    struct node
    {
        int h,s,sum;
    }Sum[M],H[M],S[M];
    bool cmp1(node a,node b)
    {
        return a.sum < b.sum;
    }
    bool cmp2(node a,node b)
    {
        return a.h < b.h;
    }
    bool cmp3(node a,node b)
    {
        return a.s < b.s;
    }
    int n,A,B,C,l,r,cnt,mins,lims,limsum,ans;
    
    int main()
    {
        n = read(),A = read(),B = read(),C = read();
        rep(i,1,n)
        {
            Sum[i].h = read(),Sum[i].s = read(),Sum[i].sum = Sum[i].h * A + Sum[i].s * B - C;
            H[i].h = Sum[i].h,H[i].s = Sum[i].s,H[i].sum = Sum[i].sum;
            S[i].h = Sum[i].h,S[i].s = Sum[i].s,S[i].sum = Sum[i].sum;
        }
        sort(Sum+1,Sum+1+n,cmp1);
        sort(H+1,H+1+n,cmp2);
        sort(S+1,S+1+n,cmp3);//上面是按照关键字排序
        rep(i,1,n)
        {
            l = 0,r = 0,cnt = 0;
            mins = S[i].s,lims = S[i].s + C / B;//确定限制范围
            rep(j,1,n)
            {
                limsum = A * H[j].h + B * mins;
                while(r < n && Sum[r+1].sum < limsum) 
                {
                    r++;
                    if(mins <= Sum[r].s && Sum[r].s <= lims) cnt++;//将合法元素选中
                }//判断合法区间的末尾
                while(l < n && H[l+1].h < H[j].h)
                {
                    l++;
                    if(mins <= H[l].s && H[l].s <= lims) cnt--;
                }//判断合法区间开头并且删除不合法元素
                ans = max(ans,cnt);//更新答案
            }
        }
        printf("%d
    ",ans);
        return 0;
    }
  • 相关阅读:
    两种常用图像识别迁移学习方法
    学习备忘
    图像处理代码举例(C++、MATLAB、OpenCV)
    Java 读取clob字段的几种方法
    Oracle 查询表注释以及字段注释
    Oracle 查询表的索引包含的字段
    Oracle 获取表的主键、外键以及唯一约束条件
    Oracle列信息表 all_tab_columns中的data_length和data_precision字段区别
    Oracle 表结构、索引以及分区信息查询
    Java中的String,StringBuilder,StringBuffer三者的区别
  • 原文地址:https://www.cnblogs.com/captain1/p/9545399.html
Copyright © 2011-2022 走看看