专项:反悔贪心

CF865D#

题意:给定股票每天的价值,一天只可以买入一股或卖出一股,求 n 天后的最大收益。

错误的贪心:在第 i 天卖出前 i1 天还没有卖出的最小价格。
用小根堆维护。

	rep(i, 1, n) {
		int x; cin >> x;
		if(!q.empty() && q.top() < x) {
			ans += x - q.top();
			q.pop();
		}
		q.push(x);
	}

对于 i<j<k,若 a[k]a[i]>a[j]a[i],而 a[i] 早在 j 时刻就被计入答案并踢出队列了,这样显然是不优的。
考虑反悔。

什么叫反悔?也就是把第 j 天的影响消除,并在候选队列里添加 i

  • 如果第 j 天选了 i 且第 k 天选了 j,由于 (a[k]a[j])+(a[j]a[i])=a[k]a[i],等同于第 k 天选了 i,相当于撤销了 j 的影响。
    仅这样处理是不够的。
    此时第 j 天的股票已经踢出队列,但实际上还没被卖出,有可能对答案有贡献的,不能踢出。

  • 如果第 j 天选了 i,上述代码中的 push(x) 实际上是把一个待反悔的 i 加入队列。
    只需要再 push(x) 一次,将 j 加入队列。

	rep(i, 1, n) {
		int x; cin >> x;
		if(!q.empty() && q.top() < x) {
			ans += x - q.top();
			q.pop();
			q.push(x);
		}
		q.push(x);
	}

P2949 [USACO09OPEN] Work Scheduling G#

题意:n 个工作,每个工作有一截止时间 ti 和价值 pi,每天能完成 1 项工作,最大化总价值。
贪心:按截止时间排序,能做就做。
反悔:不能做时,如果前面存在小于当前价值的元素,撤销操作,更新贡献。
小根堆维护已经做了的所有价值,用堆的大小表示当前时间。

int main() {
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	int n; cin >> n;
	vector<pair<int, int>> a(n);
	for(auto &[x, y] : a) cin >> x >> y;
	ranges::sort(a);
	priority_queue<ll, vector<ll>, greater<ll>> q;
	for(auto &[x, y] : a) {
		if(x > q.size()) {
			q.push(y);
		}
		else if(y > q.top()) {
			q.pop();
			q.push(y);
		}
	}
	ll ans = 0;
	while(!q.empty()) {
		ans += q.top();
		q.pop();
	}
	cout << ans;
	return 0;
}

P4053 [JSOI2007] 建筑抢修#

题意:n 个工作,每个工作有所需时间 leni 和截止时间 edi,最大化完成数量。
贪心:按截止时间排序,能做就做。
反悔:不能做时,如果前面存在 len 大于当前 len 的元素,撤销操作,更新贡献。
维护当前时间 t 和已经完成的所有 len

int main() {
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	int n; cin >> n;
	vector<pair<int, int>> a(n);
	for(auto &[ed, len] : a) cin >> len >> ed;
	ranges::sort(a);
	priority_queue<int> q;
	ll t = 0;
	for(auto &[ed, len] : a) {
		if(t + len <= ed) {
			t += len;
			q.push(len);
		}
		else {
			if(len < q.top()) {
				t += -q.top() + len;
				q.pop();
				q.push(len);
			}
		}
	}
	cout << q.size();
	return 0;
}

P2107 小Z的AK计划#

肯定是顺着走一遍最优,每个点抉择选或不选。
贪心:能选就选。
反悔:如果已经选过的点中存在思考时间大于当前位置,撤销操作。

int main() {
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	ll n, m; cin >> n >> m;
	vector<pair<ll, ll>> a(n);
	for(auto &[pos, val] : a) cin >> pos >> val;
	ranges::sort(a);
	priority_queue<ll> q;
	ll x = 0;
	for(auto &[pos, val] : a) {
		if(pos + x + val <= m) {
			x += val;
			q.push(val);
		}
		else if(!q.empty() && val < q.top()){
			x += val - q.top();
			q.pop();
			q.push(val);
		}
	}
	cout << q.size();
	return 0;
}

CF3D Least Cost Bracket Sequence#

如果 s[i]=?,先填上 ')'。
维护 tp 等于当前左括号数减右括号数。
如果 tp<0,说明左括号少了,反悔 '?' 变为 '(',选对当前贡献最大的位置。

priority_queue<pair<int, int>> q;
string s;
ll tp, ans;

int main() {
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	cin >> s;
	for(int i = 0; auto ch : s) {
		if(ch == '(') {
			++ tp;
		}
		else {	
			-- tp;
			if(ch == '?') {
				int L, R; cin >> L >> R;
				ans += R;
				s[i] = ')';
				q.emplace(R - L, i);
			}
		}
		if(tp < 0) {
			if(q.empty()) break;
			tp += 2;
			ans -= q.top().first;
			s[q.top().second] = '(';
			q.pop();
		}
		++ i;
	}
	if(tp == 0) cout << ans << '\n' << s;
	else cout << -1 << '\n';
	return 0;
}

CF730I#

先用编程能力最大的 p 名学生组成编程队,然后从 0 开始一个人一个人地构建运动队。

分类讨论:

  • 招人:从剩余学生中,选择运动能力最大的。
  • 挖人:从编程队中挖一名【运动能力减编程能力】最大的学生到运动队,然后编程队再招人,从剩余学生中,选择编程能力最大的。
    上面这两种方法,谁能让能力总和变得更大,就用哪种方法。重复执行 s 次。
#include<bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i <= (b); ++ i)
#define per(i, a, b) for(int i = (a); i >= (b); -- i)
#define pb emplace_back
using namespace std;
using ll = long long;
constexpr int N = 3e3 + 5;

int n, p, s, a[N], b[N], ans;

struct programming {
	int i;
	bool operator < (const programming &o) const {
		if(b[i] - a[i] != b[o.i] - a[o.i]) return b[i] - a[i] > b[o.i] - a[o.i];
		return i > o.i;
	}
};

struct remain_p {
	int i;
	bool operator < (const remain_p &o) const {
		if(a[i] != a[o.i]) return a[i] > a[o.i];
		return i > o.i;
	}
};


struct remain_s {
	int i;
	bool operator < (const remain_s &o) const {
		if(b[i] != b[o.i]) return b[i] > b[o.i];
		return i > o.i;
	}
};

set<programming> se;
set<remain_p> rp;
set<remain_s> rs;
set<int> ss;


int main() {
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	cin >> n >> p >> s;
	for(int i = 1; i <= n; ++ i) cin >> a[i];
	for(int i = 1; i <= n; ++ i) cin >> b[i];
	
	
	for(int i = 1; i <= n; ++ i) {
		rp.insert({i});
		rs.insert({i});	
	}
	
	for(int i = 1; i <= p; ++ i) {
		int x = rp.begin() -> i;
		se.insert({x});
		rp.erase({x});
		rs.erase({x});
	}
	for(auto [i] : se) ans += a[i];
	for(int i = 1; i <= s; ++ i) {
		int x = 0, y = 0;
		x = b[rs.begin() -> i];
		y = b[se.begin() -> i] - a[se.begin() -> i] + a[rp.begin() -> i];
		if(y < x) {
			ans += x;
			int j = rs.begin() -> i;
			rp.erase({j});
			rs.erase({j});
			ss.insert(j);
		}
		else {
			ans += y;
			int j = se.begin() -> i;
			se.erase({j});
			ss.insert(j);
			j = rp.begin() -> i;
			se.insert({j});
			rp.erase({j});
			rs.erase({j});
		}
	}
	cout << ans << '\n';
	for(auto [i] : se) cout << i << ' '; cout << '\n';
	for(auto  i  : ss) cout << i << ' ';
	return 0;
}
posted @   Lu_xZ  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示