首页 > 其他分享 >【反悔贪心】P2949WorkSchedulingG+P4053[JSOI2007]建筑抢修题解

【反悔贪心】P2949WorkSchedulingG+P4053[JSOI2007]建筑抢修题解

时间:2024-07-13 23:01:03浏览次数:19  
标签:JSOI2007 int 题解 t2 long 反悔 任务 P4053 贪心

这两天遇到了几个很神奇的题目——能反悔的贪心。赶紧记录一下。

例1(用时一定模型)

用时一定:每个任务完成的耗时都是一样的。

题面:Luogu - P2949 Work Scheduling G

大体思路是:先把所有任务按照截止时间从小到大排序,然后枚举,遇到一个能做任务的就把他做了,把他的贡献加入一个小根堆中(贪心);如果遇到一个任务不能做了,但它比之前做过的任务的收益更高(即大于小根堆的堆顶),就放弃堆顶的那个任务,做这个任务。
这样实现能保证答案最优。代码实现应该就很简洁明了了。

注:因为每完成一个工作需要的时间都是1,所以小根堆的size就是已经花费的时间。若任务i能完成,则Q.size()>a[i]的截止时间

Code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 7;

struct Node{
    int d, p; // 截止时间、获得收益
    friend bool operator < (Node p1, Node p2){
        return p1.d < p2.d; // 排序规则:按照截止日期从小到大排序
    }
}a[maxn];

void solve()
{
    int n; cin >> n;
    for(int i = 1; i <= n; i ++) cin >> a[i].d >> a[i].p;
    sort(a + 1, a + n + 1);

    priority_queue <int, vector<int>, greater<int> > Q; // 小根堆,存储已完成任务的所有收益
    ll ans = 0; //! 开long long!!!
    for(int i = 1; i <= n; i ++){
        if(a[i].d > Q.size()){ // 不超时,正常加入
            Q.push(a[i].p);
            ans += a[i].p;
        }else{ // 超时的话,如果当前任务的报酬 大于 做完的任务报酬中最小的,就替换之前的任务
            if(a[i].p > Q.top()){
                ans -= Q.top(); Q.pop();
                ans += a[i].p;
                Q.push(a[i].p);
            }
        }
    }

    cout << ans << '\n';
}

signed main()
{
    ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
    solve();
    return 0;
}

例2(价值一定模型)

价值一定:每完成一个任务,获得的积分(价值)是一样的。

再来一题:Luogu - P4053 [JSOI2007] 建筑抢修

看完第一题之后,这道题应该会灵光乍现(即使这是一道蓝题?)。可以先按照任务截止时间 T 2 T_2 T2​ 升序排序。然后从小到大枚举,如果能修理就先修上,同时把修理它要花费的时间 T 1 T_1 T1​ 存到大根堆 Q Q Q 中。一旦遇到不能修上的,就把修复耗时最大的那个(即 Q.top())报废掉,给后面的建筑留下机会。

在代码的主循环中,有人可能认为直接把当前的 a[i] 统计上答案会不妥,因为可能 a[i] 就是时间最长的那个。但如果是那种情况的话,a[i] 一定会成为 Q Q Q 的堆顶,会在后面被弹出,并不会影响答案。

Code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

struct Building{
	int t1, t2;
	friend bool operator < (Building p1, Building p2){
		return p1.t2 < p2.t2; // 按照任务的截止时间排序
	}
}a[150005];

void solve()
{
	int n; cin >> n;
	for(int i = 1; i <= n; i ++) cin >> a[i].t1 >> a[i].t2;
	sort(a + 1, a + n + 1);
	
	ll cnt = 0, tim = 0; // cnt表示修好的个数,tim表示当前时间
	priority_queue <int> Q; // 默认大根堆。存储所有修过的花费的时间
	for(int i = 1; i <= n; i ++){
		tim += a[i].t1;
		cnt ++;
		Q.push(a[i].t1);
		if(tim > a[i].t2){ // 超时了,就把之前最浪费时间的那个报废掉(也可能这一轮的a[i]刚放进去就被报废了)
			cnt --;
			tim -= Q.top(); Q.pop();
		} 
	}
	cout << cnt << '\n';
}

signed main()
{
	ios :: sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	solve();
	return 0;
}

注意:因为 1 ≤ T 1 < T 2 < 2 31 1 \le T_1 < T_2 < 2^{31} 1≤T1​<T2​<231,稍微加一加就爆 int 了,要开long long!!!

补充:上面两个题都是比较简单的**“反悔堆”模型,还有更高级(也更难)的“反悔自动机”**模型。本人功力不足,建议大家上网查阅学习。

End

拓展延申:反悔贪心是OI竞赛中的一个重要知识点,会和不会的区分非常明显。一个很好的例子是,本文中两道题都是蓝题,但个人觉得其思维难度比某些绿题甚至黄题还低。

蒟蒻本人也在仔细研究。下面两篇文章写得特别详细特别好,建议大家阅读:

  1. 详细总结反悔贪心各种模型及做法
  2. 反悔贪心刷题指南+题解

洛谷大佬整理的反悔贪心题单:『进阶方法篇』反悔贪心系列

让我们一起学习进步吧!拜拜ヾ(•ω•`)o

标签:JSOI2007,int,题解,t2,long,反悔,任务,P4053,贪心
From: https://blog.csdn.net/YLCHUP/article/details/140403770

相关文章

  • 题解:AT_abc362_c [ABC362C] Sum = 0
    很好写(15min解决)但不好讲(跟别人讲了20min)的写法QwQ……首先,咱先算出原式的范围。最小值(暂且记为\(k\))的公式就是:\[k=\sum_{i=1}^{N}L_i\]就是每一个最小可能值的和。同理,最大值(我记为\(w\))的公式就是:\[w=\sum_{i=1}^{N}R_i\]即最大可能值的和。算这玩意儿......
  • 【题解】洛谷 P10765 「CROI · R2」在相思树下 I
    I题意简述共\(T\)组测试数据。对于每一组测试数据,有初始数列,共\(n\)个元素,从\(1\)至\(n\)。给出\(k\)次操作。操作一:将数列中下标为奇数的数全部删除;操作二:将数列中下标为偶数的数全部删除。完成操作之后,将剩余的数从下标\(1\)开始依次重新编排下标。求问\(k\)次......
  • 题解:Codeforces CF1613C Poisoned Dagger
    标签:二分题意给定一个长度为\(n\)的序列\(a\),定义数\(k\),对于\(i>1\),如果\(a_i-a_{i-1}<k\),\(s\)加上\(a_i-a_{i-1}\),否则加上\(k\),求满足\(s\geqh\)的最小\(k\)。思路手玩样例,\(k\)越大龙死的越快,所以具有单调性,考虑二分答案。每次缩小范围时判断是否\(k\g......
  • 洛谷 P6522 [CEOI2010 day2] tower 题解
    [CEOI2010day2]tower题目背景古巴比伦人决定建造一座塔。题目描述这座塔共有\(n\)层,每层由一个边长为\(a_i\)的立方体石块构成。一个石块\(i\)能够直接放在石块\(j\)上当且仅当\(a_i\leqa_j+D\),其中\(D\)为一个给定的常数。你需要求出如果使用全部的石块,有多......
  • 洛谷 P2478 [SDOI2010] 城市规划 题解
    题意简述仙人掌上求至少间隔两个位置的最大独立集。(仙人掌指,没有一条边在两个或以上的环里的无向图。)题目分析仙人掌就是树套环,即树上每个结点都是一个结点或环。那么将题目拆解成树上DP和环上DP即可。用tarjan缩点即可。树上DP先来看看树上DP。显然每个点有三个状......
  • [ZJOI2006] 三色二叉树 题解
    [ZJOI2006]三色二叉树题解link趣题。首先我们把题目分成两部分:建树和dp求解。建树:观察发现,字符串中的第\(i\)个字符就代表图中的第\(i\)个节点。如果\(S_i=0\),直接跳过;如果\(S_i=1\),那么\(i+1\)是\(i\)唯一的子节点,在两点之间连边,然后继续递归建树即可。......
  • 2024年06月CCF-GESP编程能力等级认证C++编程三级真题解析
    本文收录于专栏《C++等级认证CCF-GESP真题解析》,专栏总目录:点这里。订阅后可阅读专栏内所有文章。一、单选题(每题2分,共30分)第1题小杨父母带他到某培训机构给他报名参加CCF组织的GESP认证考试的第1级,那他可以选择的认证语言有()种。A.1B.2C.3D.4答案:C第2......
  • Codeforces Round 957 (Div. 3) A-G 题解
    CodeforcesRound957(Div.3)A-G题解A.OnlyPluses枚举思路:枚举\(a\),\(b\),\(c\)增加的次数,维护最值即可。代码:#include<bits/stdc++.h>usingnamespacestd;#definefffirst#definesssecond#definepbpush_back#defineall(u)u.begin(),u.end()#......
  • 初等数论课程测试题解
    初等数论课程测试题解刚想起来传到博客园上面。正在写。Upd.20240222:已写完,欢迎查错!一、请给出整除的概念及性质对于整数\(a,b\)\((b\neq0)\),如果存在整数\(c\),使得\(a=bc\),则称\(b\)整除\(a\),记作\(b\mida\);否则称\(b\)不整除\(a\),记作\(b\nmida\)。性质......
  • [题解] CF19D Points
    [题解]CF19DPointsCF19DPoints在一个笛卡尔坐标系中,定义三种操作:addxy:在坐标系上标记一个点\((x,y)\),保证\((x,y)\)在添加前不在坐标系上。removexy:移除点\((x,y)\),保证\((x,y)\)在移除前已在坐标系上。findxy:找到所有已标记的\((x',y')\),需满......