2021牛客寒假算法基础集训营3 B 尺取法 双指针 维护双指针 模拟 贪心

本文详细介绍了使用尺取法解决一类优化问题的思路和AC代码实现。通过排序和贪心策略,确保在有限的A类名额内选取最多人数。在代码调试过程中,重点在于维护区间满足人数条件和A类限制。最终得出最小得分的解决方案。
摘要由CSDN通过智能技术生成

题目

在这里插入图片描述

题解思路

我们可以将所有人的5个等级的分数弄在一起从小到大排序,因为A等是有限的,在同分数的时候我们贪心的将A等往后面放。
例子
当尺取缺一个人的时候这个人的B类分和A类相同但是区间已经用了K个a了,
此时用B来才能取出正解。

我们的代码只在开头删除cnta,所以我们必须要让结尾最优。

尺取的时候 我们维护取到的区间满足有N个人的分数。并且必须选择A的人小于等于K个,此时就可以更新答案。

当加入一个分数的时候
将这个分数的人有多少个分数在集合++
这个分数的人没加过 cntstu++
这个分数为A类时
这个人的A在集合中的数目++
并且只有一个分数在集合中必须选择它
cnta++

当首指针往前取分数的时候
将这个分数的人有多少个分数在集合–
当这个人减到0的时候 cntstu–
当这个人减到1并且 这个人的A在集合中的数目为1时
即剩下了一个A类的分数
必须选择 cnta++
当这个被减去的分数为A类并且区间中没这个人的分数了。
cnta–

调了很久的bug
思路还是好理解的。

AC代码
#include <bits/stdc++.h>
//#include <unordered_map>
//priority_queue
#define PII pair<int,int>
#define ll long long
using namespace std ;
const  int  INF =  0x3f3f3f3f ;
const  int N = 100010 ;
int cnt = 1 ;
struct node 
{
    int x , val , p ;
}a[5*N] ;
int mp[N] ;
int mpa[N] ; 

bool cmp (node A , node B )
{
    if ( A.val == B.val )
        return A.x < B.x ;  
    return A.val < B.val ; 
}
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int k , n ;
    cin >> n >> k ;
    for (int i = 1 ; i <= n ; i++ )
    {
        int t1 , t2 , t3 , t4 , t5 ;
        cin >> t1 >> t2 >> t3 >> t4 >> t5 ;
        a[cnt] = {1,t1,i};
        cnt++;
        a[cnt] = {0,t2,i};
        cnt++;
        a[cnt] = {0,t3,i};
        cnt++;
        a[cnt] = {0,t4,i};
        cnt++;
        a[cnt] = {0,t5,i};
        cnt++;
    }
    //unordered_map <int , int > mp ; 
    //unordered_map <int , int > mpa ;
    int cntstu = 0 ; 
    int cnta = 0 ; 
    int ans = INF ; 
    int sp = 1 ; 
    cnt--;
    sort(a+1,a+1+cnt,cmp) ;
    /*for (int i = 1 ; i <= cnt ; i++ )
        cout << i << " " ; 
    cout << "\n" ;
    for (int i = 1 ; i <= cnt ; i++ )
        cout << a[i].x << " " ; 
    cout << "\n" ;
    for (int i = 1 ; i <= cnt ; i++ )
        cout << a[i].val << " " ; 
    cout << "\n" ;
    for (int i = 1 ; i <= cnt ; i++ )
        cout << a[i].p << " " ; 
    cout << "\n" ;*/
    mp[a[1].p]++;
    cntstu++;
    for (int i = 1 ; i <= cnt ; i++ )
    {
        for (; sp <= cnt ; sp++ )
        {
            if (cntstu == n && cnta <= k )
            {
                ans = min(a[sp].val - a[i].val , ans ) ; 
                break ; 
            }                            
            mp[a[sp+1].p]++;
            if (mp[a[sp+1].p] == 1 )
            {
                //cout << cntstu << " "   << a[sp+1].p << " " << mp[1] << " " << mp[2] << " " << mp[3] << "\n" ; 
                cntstu++;
            }
            if (a[sp+1].x == 1 )
            {
                mpa[a[sp+1].p]++;
                if (mp[a[sp+1].p] == 1 )
                {
                    //cout << sp+1 << " a++\n";
                    cnta++;
                }
            }
        }
        //cout << i << "   stu:" << cntstu << " a:" << cnta << "  sp:"<< sp << "\n"   ; 
        mp[a[i].p]--;
        if (mp[a[i].p] == 0 )
            cntstu--;
        if (mp[a[i].p] == 1 && mpa[a[i].p] == 1 )
        {
            cnta++;
        }
        if (a[i].x == 1 && mp[a[i].p] == 0 )
            cnta--;
        //cout << i << "   stu:" << cntstu << " a:" << cnta << "  sp:"<< sp << "\n"   ; 
    }
    cout << ans << "\n" ; 
    return 0 ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值