反悔贪心
一般的贪心是先考虑局部最优,再从局部最优求出整体最优。但是可能会遇到一些情况导致局部最优推出来不是整体最优,可能会存在更优的方案。这时候就要加入反悔操作,结合具体题目可能比较容易理解。
Work Scheduling G
这个题目需要我们求出在截至时间内,能获得的最大利润。如果我们按照每项工作的利润从大到小排序,然后依次选择的方法来写,遇到一下这个情况的时候就不是最优解了。
按照刚才的方法,我们选择第一项和第三项工作获得17的利润。但是很明显,要是按照选择二、三、一的顺序来选择,这是合法的,能获得25的利润,比刚才的方法更优。这种情况还看不出来什么,接着看下面的例子。
这个例子的最优解是按照二、三、四选择工作,得到57的利润。按照利润排序的方法行不通,那我们按照截止时间来对工作进行排序。在这个例子中,按照这个方法选择,遇到第三项工作不合法,但是选择第三项工作是比选择第二项工作更优的,这里就要进行反悔操作。要是遇到不合法的情况,就将已经选择的利润最小的工作取消,看看把当前这的工作加入进去。对于这个题来说,只要弹出了一项工作,一定就能加入一项新的工作,因为每个工作花费时间都是1。这个操作就需要用小根堆来实现。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 1e5;
void solve(){
int n;
cin >> n;
vector<PII> a(n);
for (int i = 0; i < n; i++) cin >> a[i].second >> a[i].first;
sort(a.begin(), a.end(), [&](PII x, PII y){ //按照截止时间排序
if (x.second == y.second) return x.first > y.first;
return x.second < y.second;
});
ll sum = 0;
priority_queue<PII, vector<PII>, greater<PII>> q;
for (int i = 0; i < n; i++){
if (a[i].second > q.size()){ // 如果这项工作的截止时间还没到
sum += a[i].first;
q.push(a[i]);
} else{
if (a[i].first > q.top().first){ //要是利润最低的低于本工作就更换
sum -= q.top().first;
q.pop();
q.push(a[i]);
sum += a[i].first;
}
}
}
cout << sum;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int T = 1;
//cin >> T;
while(T--){
solve();
}
return 0;
}
反悔贪心的大概思路就是按照题目的限制条件进行排序,再维护一个大(小)根堆,遇到不合法的选项就将其和堆顶元素比较,要是更优就弹出堆顶,将其放入堆中。注意,比较出来更优的一定要是合法的(即一定能被放入堆中),才能使用反悔贪心。
建筑抢修
这个题本质上和上一题没多大区别,区别在于弹出的比较条件不同。我们先按照限制条件(建筑报废时间)从小到大排序,要是遇到不能修好的建筑,就将这个建筑和已经选择修好的建筑中花费时间最久的比较,将花费时间小的放入堆中。因为我们是按照截止时间排序的,所以弹出一个修理任务之后,我们花费的时间会减小,但是这个建筑的报废时间一定要比弹出的建筑的报废时间更晚,所以一定能在这个建筑报废前修好它。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 1e5;
void solve(){
int n;
cin >> n;
vector<PII> a(n);
for (int i = 0; i < n; i++) cin >> a[i].first >> a[i].second;
sort(a.begin(), a.end(), [&](PII x, PII y){
if (x.second == y.second) return x.first < y.first;
return x.second < y.second;
});
ll time = 0, ans = 0;
priority_queue<ll> q;
for (int i = 0; i < n; i++){
if (time + a[i].first < a[i].second){
time += a[i].first;
ans++;
q.push(a[i].first);
} else{
if (a[i].first < q.top()){
time -= q.top();
q.pop();
q.push(a[i].first);
time += a[i].first;
}
}
}
cout << ans;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int T = 1;
//cin >> T;
while(T--){
solve();
}
return 0;
}