Luogu P8800 [蓝桥杯 2022 国 B] 卡牌 题解

P8800 [蓝桥杯 2022 国 B] 卡牌

我们看到这样一句话:

请问小明最多能凑出多少套牌?

很明显用贪心可以做了。

首先要理解一个东西,卡牌套数等于最少的卡牌牌数。因为一套卡牌需要所有卡牌各一张,所以对于最少的卡牌,它如果只有 x 张,则只能有 x 套卡牌含有最少的卡牌。再多的其他卡牌就没用了,所以卡牌套数等于最少的卡牌牌数。

那具体怎么贪心呢?使卡牌套数最多。由于卡牌套数等于最少的卡牌牌数,只需要尽量让最终各种卡牌数量接近即可。那就优先画数量少的卡牌,直到空卡牌用完。

每次 O(n) 选择最小的卡牌,复杂度会很高。由于这个贪心策略是要连续选择最小的,所以可以通过排序来降低复杂度。

最后还有一个问题:可画的卡牌数有限制。还是根据卡牌套数等于最少的卡牌牌数,在空卡牌够用的情况下,最终的卡牌套数取决于初始卡牌数与可画卡牌数的和最少的卡牌。所以可以判断目前求得的卡牌数是否大于每张卡牌初始卡牌数与可画卡牌数的和的最小值来解决这个问题。若小于,继续贪心。若大于,则证明空卡牌够用,但受可画的卡牌数的限制,卡牌套数只能为每张卡牌初始卡牌数与可画卡牌数的和的最小值。(好吧这一段有点难理解,可以结合代码里的注释理解)

时间复杂度为 O(nlogn+n) ,可以通过。

最后是丑陋的的代码:

#include <bits/stdc++.h>
using namespace std;
long long n,m,an=0,min1=5000000000,ans=0,a[500000],b;
int main()
{
	scanf("%lld%lld",&n,&m);
    for(int i=0;i<n;i++)scanf("%lld",&a[i]); 
    for(int i=0;i<n;i++)
        {
	    scanf("%lld",&b);
	    if(a[i]+b<min1) //这里是求出初始卡牌数与可画卡牌数的和的最小值
	       min1=a[i]+b;
	    }
    sort(a,a+n); //STL自带的排序
    ans=a[0]; //最开始就有的卡牌套数
    for(int i=0;i<n;i++)
        {
        	if(i==n-1&&an<m) //数组a扫到最后了且空卡牌还有剩余
        	   {
			   ans+=(m-an)/n; //平均分
			   if(ans>min1)ans=min1; //防止超过初始卡牌数与可画卡牌数的和的最小值
        	   break;
			   }
        	if(a[i]!=a[i+1]) //数量不相等,给前i+1张卡牌分空卡牌
        	   {
        	   an+=(i+1)*(a[i+1]-a[i]); //用去的空卡牌
        	   ans+=(a[i+1]-a[i]); //卡牌套数增加
               }
            if(an>m) //用去的空卡牌数超过了空卡牌总数
               {
               	an-=(i+1)*(a[i+1]-a[i]); //撤销增加
               	ans-=(a[i+1]-a[i]); 
               	ans+=((m-an)/(i+1)); //平均分
               	break; //接下来肯定不用再分了,可以退出了
			   }
			if(ans>min1)  //判断是否超过初始卡牌数与可画卡牌数的和的最小值
			   {
			   ans=min1; 
			   break; //可以退出了
		       }
		}
	printf("%lld",ans); //输出结果
    return 0;
}

AC记录

posted @   w9095  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
点击右上角即可分享
微信分享提示