【题解】【洛谷P11231】【贪心】——[CSP-S 2024] 决斗

P11231 [CSP-S 2024] 决斗

通往洛谷的传送门

题目描述

今天是小 Q 的生日,他得到了 n n n 张卡牌作为礼物。这些卡牌属于火爆的“决斗怪兽”,其中,第 i i i 张卡代表一只攻击力为 r i r_i ri,防御力也为 r i r_i ri 的怪兽。

一场游戏分为若干回合。每回合,小 Q 会选择某只怪兽 i i i 以及另一只怪兽 j ( i ≠ j ) j(i \neq j) j(i=j),并让怪兽 i i i 向怪兽 j j j 发起攻击。此时,若怪兽 i i i 的攻击力小于等于怪兽 j j j 的防御力,则无事发生;否则,怪兽 j j j 的防御被打破,怪兽 j j j 退出游戏不再参与到剩下的游戏中。一只怪兽在整场游戏中至多只能发起一次攻击。当未退出游戏的怪兽都已发起过攻击时,游戏结束。

小 Q 希望决定一组攻击顺序,使得在游戏结束时,未退出游戏的怪兽数量尽可能少。

输入格式

输入的第一行包含一个正整数 n n n,表示卡牌的个数。

输入的第二行包含 n n n 个正整数,其中第 i i i 个正整数表示第 i i i 个怪兽的攻击力及防御力 r i r_i ri

输出格式

输出一行包含一个整数表示游戏结束时未退出游戏的怪兽数量的最小值。

输入输出样例

输入 #1

5
1 2 3 1 2

输出 #1

2

输入 #2

10
136 136 136 2417 136 136 2417 136 136 136

输出 #2

8

说明/提示

【样例 1 解释】

其中一种最优方案为:第一回合让第 2 2 2 只怪兽向第 1 1 1 只怪兽发起攻击,第二回合让第 5 5 5 只怪兽向第 4 4 4 只怪兽发起攻击,第三回合让第 3 3 3 只怪兽向第 5 5 5 只怪兽发起攻击。此时没有退出游戏的怪兽都进行过攻击,游戏结束。可以证明没有更优的攻击顺序。

【样例 3】

见选手目录下的 duel/duel3.in 与 duel/duel3.ans。

该样例满足 ∀ 1 ≤ i ≤ n , r i ≤ 2 \forall 1 \leq i \leq n, r_i \leq 2 ∀1in,ri2

【样例 4】

见选手目录下的 duel/duel4.in 与 duel/duel4.ans。

【数据范围】

对于所有测试数据,保证: 1 ≤ n ≤ 10 5 1 \leq n \leq 10^5 1n105 1 ≤ r i ≤ 10 5 1 \leq r_i \leq 10^5 1ri105

测试点 n n n r i r_i ri特殊性质
1 ∼ 4 1\sim 4 14 ≤ 10 \leq 10 10 ≤ 10 5 \leq 10^5 105无特殊性质
5 ∼ 10 5\sim 10 510 ≤ 10 5 \leq 10^5 105 ≤ 2 \leq 2 2^
11 ∼ 15 11\sim 15 1115 ≤ 30 \leq 30 30 ≤ 10 5 \leq 10^5 105特殊性质 A
16 ∼ 20 16\sim 20 1620 ≤ 10 5 \leq 10^5 105^无特殊性质

特殊性质 A:保证每个 r i r_i ri 在可能的值域中独立均匀随机生成。

1.思路解析

    先根据题目规定一些东西:r[i] r i r_i ri表示第i张牌的“攻防值”。

    正难则反。“留在场上的最小值”不好求,可以求出“下场的最大值”,使用ans记录。

    观察题目,根据直觉,很容易想到一个明显的贪心策略: r r r小的牌去攻击 r r r小的牌。这样能够尽可能的让卡牌变少。同时,也能够将r更大的卡牌留给后面的“被进攻”的卡牌。

    接下来考虑具体实现。因为涉及到“大”,“小”比较,很容易想到先将r数组从小到大排序。

    一张牌有两种身份,一种是“进攻牌”,一种是“被进攻牌”。因此可以考虑同时枚举“进攻牌”和“被进攻牌”。但是如此时间复杂度 O ( n 2 ) O(n^2) O(n2),无法接受。

    因为根据上述贪心策略,当前“进攻牌”和“被进攻牌”通常是“紧密贴合”的(位置差不了多少),考虑使用一个指针p,记录当前等待被攻击的 r r r最小的牌的位置。然后就可以循环枚举“进攻牌”的位置i

    如果当前连第i张牌都不能把第p张牌踢出去(即r[p]>=r[i]),说明第i张牌“实力不够”,需要更强的牌,直接continue。反之,说明“进攻成功”,用变量ans记录答案,ans++即可。事实上,读者可以发现ans就等效于p,可以直接将ans替换成p

    注意最后的答案为n-ans+1

    本题还有一种更加巧妙的做法,原问题等价于把整个序列分成若干条子序列(这就是为什么标签有Dilworth定理),满足每个子序列中元素两两不同。因为只要两两不同就可以从最强的一直打到最弱的。显然不可能一次塞两个相同值进入一个子序列。
    然后可以构造了。当前这条子序列,最优的情况显然可以把所有个数还 > 0 >0 >0的值全都拿走一个。那答案显然就是众数个数了。
    如果看不懂的话读者可以了解一下Dilworth定理,代码就不放了,洛谷题解一大堆

2.AC代码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100010
int n,p,r[MAXN],ans; // p表示当前的没有被攻击的牌 
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&r[i]);
    sort(r+1,r+n+1); // 先排序 
    for(int i=1;i<=n;i++){
    	if(r[p]>=r[i])continue; // 不能攻击,跳过 
    	p++;ans++;
	}
	printf("%d",n-ans+1);
    return 0;
}

最后,制作不易,希望大家多多点赞收藏,关注下微信公众号,谢谢大家的关注,您的支持就是我更新的最大动力!
公众号上会及时提供信息学奥赛的相关资讯、各地科技特长生升学动态、还会提供相关比赛的备赛资料、信息学学习攻略等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝胖子教编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值