noip模拟题11.5

本文是NOIP模拟题的解析,包括T1大天使之剑、T2保卫萝卜和T3打地鼠三个问题。T1中,小A面对怪物,通过普通攻击、重击和群体攻击来降低怪物生命值,目标是最小化生命值消耗。T2中,小A在萝卜地中画咒符防止小B偷萝卜,需要计算不受威胁的萝卜面积。T3中,小A打地鼠,需要确定最多能打到多少只地鼠。每个问题都涉及贪心策略和算法实现,如前缀和、BFS和LIS等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

T1 大天使之剑

【问题描述】

小A在游戏⾥打怪。有⼀次,他⼀下⼦遇到了n个怪物。
每个怪物有一个生命值,第i个怪物的生命值是h_i。而小A除了生命值之外,还有一个属性是魔法值m。
小A和怪物们依次行动。每一回合,小A先行动,然后怪物们同时行动。
小A每次可以选择以下行动之一:
•普通攻击:令某个怪物的生命值减少1。
•重击:消耗1魔法值,令某个怪物的生命值减少2。
•群体攻击:消耗1魔法值,令全体怪物的生命值减少1。
而每个存活的怪物(生命值严格大于0)每次会令小A的生命值减少1。
假设小A有足够的生命值来维持存活,小A想知道自己至少需要被消耗多少生命值。

【输入文件】

输入文件为zhijian.in。
第一行为两个数n和m。
第二行为n个整数,第i个数为h_i。

【输出文件】

输出文件为zhijian.out。
输出一个整数,即小A至少被消耗的生命值。

【输入样例1】

2 1
2 1

【输出样例1】

1

【输入样例2】

3 4
2 4 4

【输出样例2】

6

【数据规模和约定】

对于20%的数据,m≤0;
对于30%的数据,m≤1;
对于50%的数据,m≤18;
存在30%的数据,n≤50,h_i≤50;
m=0与m=18各存在1个测试点,n≤1000,h_i≤1000;
对于100%的数据,1≤n≤100,000,0≤m≤100,0<h_i≤100,000。

一眼贪心无疑,问题是该怎么贪。
当还有魔法值时肯定是先群攻或重击。举些例子后可以很轻松地发现:当剩下的怪物大于2个时或当前怪物生命值为1肯定是群攻,否则为重击;魔法值为0时直接模拟即可(也可以不模拟,用个类似前缀和的形式直接算)。
代码(讨论了很多,可能有点丑):

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
long long h[100005],num[100005],judge[100005],cnt[100005],bb[100005];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){
  if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int main()
{
    freopen("zhijian.in","r",stdin);
    freopen("zhijian.out","w",stdout);
    int n=read(),m=read(),tot=0;
    long long ans=0,opp=0;
    for(int i=1;i<=n;i++)
    {
        scanf(AUTO,&h[i]);
        num[h[i]]++;
        if(!judge[h[i]])
        {
            judge[h[i]]=1;
            cnt[++tot]=h[i];
        }
    }
    sort(cnt+1,cnt+tot+1);
    int left=n;
    while(m>0)
    {
        if(left>2)
        {
            m--;
            ans+=left-num[1];
            left-=num[1];
            if(num[1]>0)
            {
                num[1]=0;
                cnt[1]=cnt[2]-1;
                for(int i=2;i<=tot;i++)
                {
                    if(num[cnt[i]]>0)
                    {
                        num[cnt[i]-1]=num[cnt[i]];
                        num[cnt[i]]=0;
                        if(i!=tot)
                            cnt[i]=cnt[i+1]-1;
                    }
                }
                cnt[tot]=0;
                tot--;
            }
            else
            {
                for(int i=1;i<=tot;i++)
                {
                    if(num[cnt[i]]>0)
                    {
                        num[cnt[i]-1]+=num[cnt[i]];
                        num[cnt[i]]=0;
                        cnt[i]-=1;;
                    }
                }
            }
        }
        else
        {
            if(left==2)
            {
                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值