今天一无所获

好累,又要熄灯了,又是荒芜的一天,什么也没学到,真不知道什么时候可也把spring学完,抓紧吧 ,明天图书馆猫一天,谁都别找我,找我我也不理!!!
#include<bits/stdc++.h> using namespace std; #define int long long bool cmp(pair<int,int> a,pair<int,int> b) { if(a.second==b.second) return a.first>b.first; return a.second>b.second; } signed main () { int t; cin>>t; while(t--) { int n,m; cin>>n>>m; vector<int> a(n+1,0); priority_queue<int> q; for(int i=1;i<=n;i++) { cin>>a[i]; q.push(a[i]); } vector<pair<int,int>> b(m+1); for(int i=1;i<=m;i++) { cin>>b[i].first; } for(int i=1;i<=m;i++) { cin>>b[i].second; } sort(b.begin()+1,b.end(),cmp); int ans=0; for(int i=1;i<=m;i++) { //cout<<q.top()<<" "<<b[i].first<<endl; if(q.top()>=b[i].first) { ans++; if(b[i].second) { if(b[i].second>q.top()) { q.pop(); q.push(b[i].second); } } else { q.pop(); } } } cout<<ans<<endl; } }DeepL 翻译    你现在在一个有 n 把剑的地牢里,面对着 m 个怪物。 第 i 把剑的伤害是 ai ,第 i 只怪物的生命值是 bi 。当且仅当 x≥y 时,伤害为 x 的剑可以杀死生命值为 y 的怪物。 用伤害为 x 的剑杀死 i -th的怪物后,这把剑就会消失。然后,如果 ci>0 ,你将获得一把伤害为 max(x,ci) 的新剑;否则,你将一无所获。 现在你想知道最多可以杀死多少只怪物。注意,每只怪物你最多只能杀死一次。DeepL 翻译    输入 每个测试包含多个测试用例。第一行包含测试用例的数量 T ( 1≤T≤104 )。测试用例说明如下。 每个测试用例的第一行包含两个整数 n 和 m ( 1≤n,m≤2⋅105 )。 每个测试用例的第二行包含 n 个整数 a1,a2,…,an ( 1≤ai≤109) 。 每个测试用例的第三行包含 m 个整数 b1,b2,…,bm ( 1≤bi≤109) 。 每个测试用例的第四行包含 m 个整数 c1,c2,…,cm ( 0≤ci≤109) . 保证所有测试用例中 n 和 m 的总和分别不超过 2⋅105 。帮我改一下当q.top()大于那个second最大时找到一个最小大于他的firstd的和他匹配
11-08
<think>题目背景:地牢中有n把剑和m个怪物。每把剑有伤害值ai,每个怪物有生命值bi和奖励值ci。 规则:用伤害值x的剑杀死生命值y的怪物需要满足x>=y。杀死怪物后,剑消失,若ci>0则获得一把新剑,新剑伤害值为max(x, ci)(注意:这里新剑的伤害值是基于使用的剑的伤害x和ci的较大者,但题目描述中写的是max(x,ci));否则没有新剑。 目标:求最多能杀死的怪物数量。 给定代码思路: 1. 使用一个大根堆(优先队列)q来存储当前所有的剑的伤害值(初始的n把剑)。 2. 将m个怪物表示为(b[i].first, b[i].second),其中b[i].first是生命值(即上面的bi),b[i].second是奖励值(即上面的ci)。 3. 对怪物数组b(从下标1到m)按照自定义比较函数cmp排序: cmp: 先比较second(奖励值),从大到小;若奖励值相同,则按照first(生命值)从大到小。 4. 然后从怪物数组的第一个开始遍历(即从奖励值最大的怪物开始): 当前堆顶(最大伤害的剑)如果大于等于当前怪物的生命值(b[i].first),则可以杀死: ans++ (计数) 然后处理奖励: - 如果奖励值b[i].second>0,并且奖励值大于当前堆顶(注意:这里代码中写的是b[i].second>q.top()),那么我们就用这把新剑替换当前使用的剑(即弹出堆顶,将b[i].second推入堆中)。否则,我们只弹出堆顶而不加入新剑(因为如果奖励值小于等于当前堆顶,那么新剑伤害值=max(原来的剑,ci)<=原来的堆顶?但这里注意,实际上新剑伤害值应该是max(使用的剑, ci),而使用的剑就是堆顶。所以如果ci<=使用的剑,那么新剑伤害值等于使用的剑,但是我们已经弹出了,所以相当于没有增加新剑(因为新剑伤害值和原来一样,但我们弹出了一个,再压入一个一样的,堆的大小不变?)然而,这里代码中并没有这样处理,而是只有当b[i].second>q.top()的时候才压入新剑。否则,即使b[i].second>0,只要不大于堆顶,就不压入(但是注意,新剑伤害值应该是max(当前使用的剑, ci),而当前使用的剑就是堆顶,所以如果ci<=堆顶,那么新剑伤害值等于堆顶,所以相当于我们只是用一把相同伤害的剑替换了,但实际上我们并没有增加新的剑(因为我们弹出了一个,又压入了一个相同的,堆的大小不变)。但是,如果ci>0且小于等于堆顶,代码中并没有压入,这样就会导致堆的大小减少1?这显然不符合规则(因为规则是获得一把伤害为max(x,ci)的新剑,这里x是堆顶,所以新剑伤害等于堆顶,应该补回来)。 然而,题目代码中处理奖励的部分如下: if(b[i].second) { if(b[i].second>q.top()) { q.pop(); q.push(b[i].second); } } else { q.pop(); } 这里存在一个逻辑错误:当b[i].second>0且小于等于q.top()时,实际上应该弹出堆顶然后压入max(q.top(), b[i].second)(即堆顶,因为堆顶>=b[i].second),这样堆的大小不变。但是代码中对于b[i].second>0且不大于堆顶的情况,没有做任何操作(既没有弹出也没有压入),这就会导致堆顶没有被消耗(也就是说我们杀死了怪物,但是剑没有消失?)这显然不符合题意(因为杀死怪物后剑会消失,然后根据奖励获得新剑)。 所以代码中的处理是有问题的。正确的处理应该是: 无论奖励值如何,杀死一个怪物后,使用的剑(即堆顶)都会消失。 然后,如果ci>0,那么会得到一把新剑,伤害值为max(使用的剑, ci)(注意:使用的剑就是堆顶,所以新剑伤害值要么等于堆顶(当ci<=堆顶)要么等于ci(当ci>堆顶))。 因此,正确的操作步骤: 1. 弹出堆顶(表示使用了这把剑,剑消失)。 2. 如果ci>0,则压入max(使用的剑, ci)(但使用的剑已经弹出了,所以我们可以用max(old_top, ci)来代替)。 但是,在代码中,我们无法在弹出后立即得到弹出的值(因为弹出操作会移除堆顶)。因此,我们需要在弹出前记录堆顶的值。 修改建议: 在杀死怪物时: int top = q.top(); // 记录当前使用的剑的伤害值 q.pop(); // 剑消失 if (b[i].second > 0) { int new_sword = max(top, b[i].second); // 新剑的伤害值 q.push(new_sword); } // 如果b[i].second==0,则只弹出,不压入 这样就可以正确模拟剑的消失和新剑的获得。 另外,题目要求:当q.top()大于那个second最大时,找到一个最小大于他的first的和他匹配。 这句话的意思可能是:在怪物排序时,我们希望优先处理奖励值高的怪物,但是当奖励值相同时,我们按照生命值从大到小排序(原代码是这样)。但是,在匹配时,我们希望用尽可能小的剑去杀死怪物(保留大剑给后面更难杀的怪物),所以这里我们使用贪心:尽量用当前最大的剑去杀当前怪物(因为这样我们可能得到一把更大的剑,但也不一定,因为奖励值可能很小)。然而,题目要求是最大化杀怪数量,所以我们应该尽可能多地杀怪,而不一定是保留大剑。 但是,原代码的贪心策略是: 先按奖励值从大到小排序怪物(奖励值大的优先),奖励值相同则按生命值从大到小排序(生命值大的优先)。 然后,对于每个怪物,用当前最大的剑(堆顶)去尝试杀: 如果堆顶>=怪物生命值,则杀,然后更新堆(如上所述,但原代码更新有问题)。 为什么按奖励值从大到小排序?因为奖励值高的怪物可能给我们带来更好的剑(即更高的伤害值),这样后续能杀更多的怪物。所以优先杀奖励值高的怪物是合理的。 然而,题目要求修改:当q.top()大于那个second最大时,找到一个最小大于他的first的和他匹配。 这句话可能意味着:在同等条件下(比如奖励值都很大),我们不应该只按生命值从大到小排序,而是希望用最小的代价(即用刚刚大于怪物生命值的剑)去杀,以保留更大的剑给后面。但是,原代码中并没有这个操作,它总是用最大的剑去杀。 因此,我们需要改变匹配策略:不是每次用最大的剑,而是用一把大于等于怪物生命值的最小剑(这样保留更大的剑给后面生命值更高的怪物)。但是,我们当前的数据结构是一个大根堆,无法快速得到最小的大于等于某个值的剑。 所以,我们需要改变数据结构: 我们可以用一个multiset来存储所有的剑(初始的n把剑),然后按从小到大排序,这样我们可以用lower_bound来找到最小的大于等于怪物生命值的剑。 但是,题目中n和m最大为2e5,而且测试用例总数n和m的和不超过2e5(注意:保证所有测试用例中n和m的总和分别不超过2e5,所以总共的n和m都不超过2e5),所以我们可以使用multiset。 修改思路: 1. 将优先队列(大根堆)改为multiset(默认从小到大排序)。 2. 初始将所有剑的伤害值插入multiset。 3. 对怪物进行排序:仍然按照奖励值从大到小排序,奖励值相同的按生命值从大到小排序?但是为了节约剑,我们希望生命值小的怪物更容易被杀死,所以实际上按生命值从小到大排序可能更好?但这里我们仍然希望优先处理奖励值高的怪物。所以排序规则不变。 4. 遍历每个怪物,对于当前怪物,在multiset中二分查找第一个大于等于怪物生命值b[i].first的剑(即最小满足条件的剑)。如果找到了,则使用这把剑杀死怪物,然后: 从multiset中删除这把剑。 如果奖励值ci>0,则添加新剑:新剑伤害值 = max(使用的剑的伤害, ci) 否则,不添加。 5. 计数。 为什么这样更优?因为我们用最小的满足条件的剑去杀怪物,保留了更大的剑给后面生命值更高的怪物,这样可以杀更多的怪物。 但是,排序规则是否需要调整? 原排序:按奖励值降序,相同则按生命值降序。 现在,我们希望奖励值高的优先被处理(因为可能获得更好的剑),所以奖励值高的先处理。但是,在奖励值相同的情况下,我们应该让生命值小的先处理(因为生命值小的更容易被杀死,这样我们就能用更小的剑杀它,保留大剑)?但原代码是生命值大的先处理。 因此,我们需要调整排序规则:对于奖励值相同的怪物,按生命值升序排序(这样生命值小的在前面,优先用小的剑杀)。 所以,修改cmp函数: bool cmp(pair<int,int> a, pair<int,int> b) { if (a.second == b.second) { return a.first < b.first; // 奖励值相同,按生命值升序 } return a.second > b.second; // 奖励值降序 } 然后,我们使用multiset来维护当前所有的剑。 注意:一个怪物匹配一把剑,匹配后剑消失,然后根据奖励获得新剑(新剑加入multiset)。 算法步骤: multiset<int> s; 将初始的n把剑插入s(注意剑的伤害值,输入中是a1,...,an) 对怪物数组b(从1到m)按照自定义的cmp排序(先按奖励值降序,相同则按生命值升序) int ans=0; for (int i=1; i<=m; i++) { // 在s中查找第一个大于等于b[i].first的剑 auto it = s.lower_bound(b[i].first); if (it == s.end()) { // 没有满足条件的剑,跳过这个怪物 continue; } // 找到了,杀死怪物 ans++; int sword = *it; // 使用的剑的伤害值 s.erase(it); // 剑消失 if (b[i].second > 0) { // 新剑伤害值 = max(sword, b[i].second) int new_sword = max(sword, b[i].second); s.insert(new_sword); } } 最后输出ans。 但是,注意题目输入:怪物数组b有三个属性,但题目输入是分开的:第三行是b1,...,bm(生命值),第四行是c1,...,cm(奖励值)。 在代码中,我们定义的是vector<pair<int,int>> b(m+1); 第一个元素是生命值(first),第二个元素是奖励值(second)。 另外,注意题目中总测试用例的n和m的总和不超过2e5,所以使用multiset(每次操作O(log n))的总复杂度为O((n+m) log (n+m)),可以接受。 因此,修改后的代码: 注意:由于总测试用例数T<=10000,但n,m的总和<=200000,所以我们可以对每个测试用例使用上述算法。 修改代码: 1. 将优先队列改为multiset。 2. 修改cmp函数:当奖励值相同时,按生命值升序(即first小的在前)。 3. 按照上述步骤遍历怪物。 但是,原代码中怪物数组b是从下标1开始的,所以排序也要从b.begin()+1到b.end()。 修改后的代码: #include <bits/stdc++.h> using namespace std; #define int long long // 修改cmp:按second(奖励值)降序,若相同则按first(生命值)升序 bool cmp(pair<int, int> a, pair<int, int> b) { if (a.second == b.second) { return a.first < b.first; } return a.second > b.second; } signed main() { ios::sync_with_stdio(false); cin.tie(0); int t; cin >> t; while (t--) { int n, m; cin >> n >> m; vector<int> a(n); for (int i = 0; i < n; i++) { cin >> a[i]; } vector<pair<int, int>> b(m); // 现在b下标从0到m-1 // 输入生命值(b[i].first)和奖励值(b[i].second) for (int i = 0; i < m; i++) { cin >> b[i].first; } for (int i = 0; i < m; i++) { cin >> b[i].second; } // 使用multiset存储剑的伤害值 multiset<int> s; for (int i = 0; i < n; i++) { s.insert(a[i]); } // 对怪物数组b排序,使用自定义cmp(注意:b有m个元素) sort(b.begin(), b.end(), cmp); int ans = 0; for (int i = 0; i < m; i++) { int life = b[i].first; int reward = b[i].second; // 在multiset中找到第一个大于等于生命值的剑 auto it = s.lower_bound(life); if (it == s.end()) { // 没有剑能杀死这个怪物 continue; } ans++; int sword_used = *it; s.erase(it); // 删除这把剑 if (reward > 0) { // 获得新剑,伤害值为max(sword_used, reward) int new_sword = max(sword_used, reward); s.insert(new_sword); } } cout << ans << endl; } } 但是,注意题目输入格式:每个测试用例的第一行是n和m,第二行n个整数(a1...an),第三行m个整数(b1...bm,即怪物的生命值),第四行m个整数(c1...cm,即奖励值)。 我们定义b为vector<pair<int,int>>(m),然后第三行的生命值读入到b[i].first,第四行的奖励值读入到b[i].second。 另外,注意:原题中“新剑”的伤害是max(x, ci),其中x是使用的剑的伤害,ci是奖励值。所以这里使用max(sword_used, reward)是正确的。 但是,原题中奖励值ci可能为0,表示没有新剑,所以我们只在reward>0时添加新剑。 注意:题目中说“如果ci>0,则获得一把伤害为max(x,ci)的新剑”,所以当ci>0时,我们添加新剑。 这样修改后,我们使用了贪心策略:优先处理奖励值高的怪物(因为排序在前),并且在处理每个怪物时,使用最小满足条件的剑(以保留大剑给后面生命值更高的怪物),这样能最大化杀怪数量。 为什么这样贪心是正确的? - 优先处理奖励值高的怪物:我们希望尽早拿到高伤害的剑,这样后续可以杀生命值更高的怪物。 - 用最小满足条件的剑:这样不会浪费大剑,大剑可以留给生命值更高的怪物。 因此,我们修改代码如上。 注意:原题中总测试用例的n和m总和不超过2e5,所以使用multiset进行m次操作(每次O(log n))是可行的。 最后,输出ans。 但是,原题中保证所有测试用例的n总和不超过2e5,m总和不超过2e5,所以我们的总操作次数是O((n+m) log (n+m)),在2e5*log2(2e5)大约是2e5*18=3.6e6,可以接受。 我们重写代码并测试样例。 由于题目没有给出样例,这里需要自己注意。 另外,原题中下标从1开始,修改后的代码下标从0开始,这样更简洁。 修改后的完整代码: </think>根据问题描述和代码分析,需要修改匹配策略:优先使用满足条件的最小伤害值剑(而非最大伤害值剑)来保留高伤害剑应对后续高生命值怪物。修改如下: 1. **数据结构变更**:改用 `multiset` 替代优先队列,以便二分查找最小满足条件的剑 2. **排序逻辑调整**:怪物排序改为奖励值降序 + 生命值升序(奖励值相同时优先处理低生命怪物) 3. **匹配策略优化**:使用 `lower_bound` 查找最小伤害值 ≥ 怪物生命值的剑 ```cpp #include <bits/stdc++.h> using namespace std; #define int long long // 修改比较函数:奖励值降序,奖励值相同时生命值升序 bool cmp(pair<int, int> a, pair<int, int> b) { if (a.second == b.second) return a.first < b.first; return a.second > b.second; } signed main() { ios::sync_with_stdio(false); cin.tie(0); int t; cin >> t; while (t--) { int n, m; cin >> n >> m; vector<int> a(n); for (int i = 0; i < n; i++) cin >> a[i]; vector<pair<int, int>> b(m); for (int i = 0; i < m; i++) cin >> b[i].first; // 生命值 for (int i = 0; i < m; i++) cin >> b[i].second; // 奖励值 // 使用multiset存储剑(自动排序) multiset<int> swords; for (int val : a) swords.insert(val); // 按新规则排序怪物 sort(b.begin(), b.end(), cmp); int ans = 0; for (auto& monster : b) { int life = monster.first; int reward = monster.second; // 找到最小伤害值≥怪物生命值的剑 auto it = swords.lower_bound(life); if (it == swords.end()) continue; // 无可用剑 ans++; int used_sword = *it; swords.erase(it); // 移除使用的剑 // 处理奖励:ci>0时获得新剑 if (reward > 0) swords.insert(max(used_sword, reward)); } cout << ans << endl; } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值