牛客练习赛87 总结

目录​​​​​​​

A-中位数(贪心)

题目链接

题意

思路

代码

B-k小数查询(尺取法)

题目链接

题意

思路

代码

C-牛老板(记忆化搜索+贪心)

题目链接

题意

思路

代码

收获


 

A-中位数(贪心)

题目链接

        A-中位数_牛客练习赛87 (nowcoder.com)

题意

  

思路

        其实就是一串数字,删去一个加到另一个上,求中位数,这里的中位数如果总数是偶数的话那么就去中间靠右的,实际上按照题目的式子即可。

        很容易能用贪心得到每一次最优的方案就是让次大的加到最大的上,最大的一直堆,那么经过k次就减少k个数,这里特判一下如果只有一个的话,那么就输出总数。

代码

inline void Case_Test()
{
    cin>>n>>k;
    sum=0;
    for (int i=1;i<=n;i++)
    {
        cin>>a[i];
        sum+=a[i];
    }
    sort(a+1,a+1+n);
    if (n-k==1) cout<<sum<<endl;
    else cout<<a[ (n-k+1)/2 ]<<endl;
}

B-k小数查询(尺取法)

题目链接

        B-k小数查询_牛客练习赛87 (nowcoder.com)

题意

思路

        我一开始看感觉是单调队列,一个队列维护满足条件的情况,但是我还是太年轻了,这样完全行不通,不能得到所有满足条件的区间个数。

        后面就用尺取法,在博客里之前也遇到一个尺取法。

尺取法,一种神奇的技巧。在Codeforces中显示它的算法名称叫做"two pointers", 直译成中文的话叫双指针法。 顾名思义,像尺子一样取一段,尺取法通常是对数组保存一对下标,即所选取的区间的左右端点,然后根据实际情况不断地推进区间左右端点以得出答案。

         上次那个题给了我这个题灵感,如果遇到一个满足条件的,求个数,实际上就会有两个指针在这个[l,r]区间上往左往右,那么个数就是(suml+1)\times(sumr+1),那么在这里题里面首先可以找到x所在的pos位置,然后就对每一个a[i]<x的情况下分隔开,如果满足条件的话我们就加上要求的。

        这里我一开始是分成左右两个区间,也就是小于pos的满足的,还有大于pos满足的,这里值得反思反思。实际上我可以pos左边的贡献a个小于x的点,pos右边贡献b个小于x的点,所以我们需要每次移动,l++,r++,当不满足条件的时候就不必要进行了,后面更不会满足条件了。

        再处理point数组(也就是存多少个),这个多少个是这么理解的,在pos左边的就看它左边还有多少个满足条件的,如果是在pos右边的那就看它右边有多少个满足条件的,因为我们的suml和sumr就是这样处理的。

        左边是当前a[i]<x的点向左的,因为用尺取法的时候是看它左边还有多少个满足条件的,反之则反。

代码

inline void Case_Test()
{
    cin>>n>>x>>k;
    for (int i=1;i<=n;i++)
    {
        cin>>a[i];
        if (a[i]==x) pos=i;//pos记录x所在位置
    }
    num=0;
    cnt=0;
    for (int i=1;i<pos;i++)
    {
        if (a[i]<x) point[++num]=cnt,cnt=0;
        else cnt++;
        //左边是当前a[i]<x的点向左的
        //因为用尺取法的时候是看它左边还有多少个满足条件的
    }
    p=++num;
    cnt=0;
    for (int i=pos+1;i<=n;i++)
    {
        if (a[i]<x) point[num++]=cnt,cnt=0;
        else cnt++;
        //同理,右边的点是看它右边还有多少个
        //但是统计的时候已经过了这个点,所以我选择先存数值再++
    }
    point[num]=cnt;
    int l=max(p-k+1,(int)1),r=min(num,l+k-1);
    //这里是看是否越界,先确定l,再确定r
    while (r-l+1==k&&r<=num&&r>=p&&l<=p)
    {
        if (r==p)
        {
            cnt=0;
            for (int i=pos+1;i<=n;i++) 
                if (a[i]>x) cnt++;
                else break;
            ans+=(cnt+1)*(point[l]+1);
        }
        else if (l==p)
        {
            cnt=0;
            for (int i=pos-1;i>=1;i--) 
                if (a[i]>x) cnt++;
                else break;
            ans+=(cnt+1)*(point[r]+1);
        }
        //因为point[p]这个pos处的特殊点没有处理,所以在这里暴力一下
        else
        {
            ans+=(point[l]+1)*(point[r]+1);
        }
        l++;r++;
        //移动区间
    }
    cout<<ans;
}

C-牛老板(记忆化搜索+贪心)

题目链接

        C-牛老板_牛客练习赛87 (nowcoder.com)

题意

思路

        这个题是后面补的,我也想过记忆化搜索,但是没想到这个贪心的,我想到的是6出一点,但不是最多,然后9出一点,也不是最多,这样加起来正好是n。但是仔细一想,还是得贪心找当前最大的6的次方,或者当前最大的9的次方,然后去取min,这样贪心是最优的,假如有一个6^a不选,去选一个6^{a-1},那么剩下了5个6^{a-1};同理,9^b也不选,去选9^{b-1},也空出了8个9^{b-1},那么这两个空出来的不是一个系数能够解决的,所以还得是取最大的。 

         然后就是用map用记忆化搜索了。

代码

unordered_map<int,int>mp;
inline void init()
{
    for (int i=1;i<=N;i*=6)
        m6[++cnt1]=i;
    m6[++cnt1]=inf;
    for (int i=1;i<=N;i*=9)
        m9[++cnt2]=i;
}
inline int dfs(int n)
{
    if (n<6) return n;
    if (mp[n]) return mp[n];
    int t1=upper_bound(m6+1,m6+1+cnt1,n)-m6;
    int t2=upper_bound(m9+1,m9+1+cnt2,n)-m9;
    return mp[n]=min(dfs(n-m6[t1-1]),dfs(n-m9[t2-1]))+1;
}
inline void Case_Test()
{
    cin>>x;
    cout<<dfs(x)<<endl;
}

收获

        C题中用unordered_map<int,int>mp 108ms

        用map<int,int>mp 272ms

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值