几道有趣的贪心题

思路:

首先,对于两个人a,b我们判断要抢A给B还是抢B给A,假设前面的策略比较好m(a)-p(b)>=m(b)-p(a),即m(a)+p(a)>=m(b)+p(b).于是我们先按照m+p从大到小排序.注意到答案有单调性,二分抢的人数k,

然后我们可以先选好最小的k个m+p的p,其余的n-k选出最大的m,然后调整,从n-k个中调整被资助的人.

#include <bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(0), cin.tie(0) 
#define ll long long 
#define double long double
#define ull unsigned long long 
#define PII pair<int, int> 
#define PDI pair<double, int> 
#define PDD pair<double, double> 
#define debug(a) cout << #a << " = " << a << endl 
#define point(n) cout << fixed << setprecision(n)
#define all(x) (x).begin(), (x).end() 
#define mem(x, y) memset((x), (y), sizeof(x)) 
#define lbt(x) (x & (-x)) 
#define SZ(x) ((x).size()) 
#define inf 0x3f3f3f3f 
#define INF 0x3f3f3f3f3f3f3f
#define writen(x) write(x),printf("\n")
using namespace std;
const int N = 2e6 + 10;
int n;
struct rec {
	int a, b;
	bool operator <(const rec& i) const {
		return a + b > i.a + i.b;
	}
}a[N];
bool check(int x) {
	priority_queue<PII> hrob, hgive;
	vector<char> vis(n + 1, 0);
	for (int i = 1; i <= n - x; ++i) hrob.emplace(a[i].a, i);
	int sa = 0, sb = 0 ;
	for (int i = 1; i <= x; ++i) {
		sa += hrob.top().first;
		vis[hrob.top().second] = 1;
		hrob.pop();
	}
	for (int i = n - x + 1; i <= n; ++i) hgive.emplace(a[i].b, i), sb += a[i].b;
	if (sa >= sb) return true;
	priority_queue<PII> heap;
	for (int i = 1; i <= n - x; ++i) if (!vis[i]) heap.emplace(a[i].a, i);
	for (int i = n - x; i >= x + 1; --i) {
		int tt = hgive.top().first;
		if (a[i].b < tt) {
			sb -= tt, sb += a[i].b;
			hgive.pop(); hgive.emplace(a[i].b, i);
		}
		if (vis[i]) {
			sa -= a[i].a;
			auto t = heap.top(); heap.pop();
			while (t.second >= i) {
				t = heap.top(); heap.pop();
			}
			vis[t.second] = 1, vis[i] = 0;
			sa += t.first;
		}
		if (sa >= sb) return true;
	}
	return false;
}
signed main() {
	IOS;
	cin >> n;
	for (int i = 1; i <= n; ++i) cin >> a[i].a;
	for (int i = 1; i <= n; ++i) cin >> a[i].b;
	sort(a + 1, a + 1 + n);
	int l = 0, r = n / 2;
	while (l < r) {
		int mid = (l + r + 1) >> 1;
		if (check(mid)) l = mid;
		else r = mid - 1;
	}
	cout << l << "\n";
}

思路:

首先我们得特判:如果A!=A+AB+BA,或者B!=B+AB+BA(数量),那么肯定无解.

接下来我们有个显然的贪心,选出尽可能多的双字符,即ab和ba

我们可以将字符串分成若干个子串,每个子串相邻字符都不同:

即ababab...

1.如果abababa或babababa为偶数,那么全部变成ab和ba是最好的

2.如果abababa.为奇数,先存下来,调整

然后我们先在尽可能保证cntab>=ab的前提下分配给cntba,然后再对bababa去掉头/尾然后转化一部分给ab,有可能cntab比较多,可以用同样办法给cntba.

#include <bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(0), cin.tie(0) 
#define ll long long 
#define double long double
#define ull unsigned long long 
#define PII pair<int, int> 
#define PDI pair<double, int> 
#define PDD pair<double, double> 
#define debug(a) cout << #a << " = " << a << endl 
#define point(n) cout << fixed << setprecision(n)
#define all(x) (x).begin(), (x).end() 
#define mem(x, y) memset((x), (y), sizeof(x)) 
#define lbt(x) (x & (-x)) 
#define SZ(x) ((x).size()) 
#define inf 0x3f3f3f3f 
#define INF 0x3f3f3f3f3f3f3f
#define writen(x) write(x),printf("\n")
using namespace std;
bool cmp(int a, int b) {
	return a > b;
}
void solve() {
	string s;
	int A, B, AB, BA, cntAB = 0, cntBA = 0;
	vector<int> ab, ba, odd;
	cin >> A >> B >> AB >> BA >> s;
	s = "?" + s;
	if (count(all(s), 'A') != A + AB + BA || count(all(s), 'B') != B + BA + AB) return cout << "NO\n", void(0);
	int n = s.size() - 1;
	for (int i = 1; i <= n; ++i) {
		int j = i;
		while (j + 1 <= n && s[j] != s[j + 1]) ++j;
		int len = j - i + 1;
		if (len % 2 == 0) {
			if (s[i] == 'A') cntAB += len / 2, ab.emplace_back(len / 2);
			else cntBA += len / 2, ba.emplace_back(len / 2);
		}else {
			odd.emplace_back(len / 2);
		}
		i = j;
	}
	//在尽可能满足cntAB>=AB的前提下,使得cntAB尽量大
	for (int x : odd) {
		int t = min(max(0ll, AB - cntAB), x);
		cntAB += t;
		cntBA += x - t;
	}
	sort(all(ab), cmp);
	sort(all(ba), cmp);
	//在cntBA>=BA的前提下,把多余的BA分给AB
	for (int x : ba) {
		if (cntBA > BA) {
			--cntBA, --x;
			int t = min(cntBA - BA, x);
			cntBA -= t;
			cntAB += t;
		}
	}
	//在cntAB>=AB的前提下,把多余的AB分给BA
	for (int x : ab) {
		if (cntAB > AB) {
			--cntAB, --x;
			int t = min(cntAB - AB, x);
			cntAB -= t;
			cntBA += t;
		}
	}
	cout << ((cntAB >= AB && cntBA >= BA) ? "YES" : "NO") << "\n"; 
}
signed main() {
	IOS;
	int T;
	cin >> T;
	while (T--) solve();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 目描述:有一组气球,每个气球都有一个起始点和一个结束点,同一时间内只能有一个气球飞行。现在需要尽可能多地放置气球,求最多可以放置多少个气球。 解法:对于每个气球,按照结束点从小到大排序,依次遍历每个气球,如果当前气球的起始点大于等于上一个气球的结束点,则可以放置,否则不能放置。采用贪心思想,每次选择结束点最靠前的气球,可以得到最多可以放置的气球数量。 Java代码: ```java public int maxBalloons(int[][] balloons) { if (balloons == null || balloons.length == 0) { return 0; } Arrays.sort(balloons, (a, b) -> a[1] - b[1]); int count = 1; int end = balloons[0][1]; for (int i = 1; i < balloons.length; i++) { if (balloons[i][0] >= end) { count++; end = balloons[i][1]; } } return count; } ``` 2. 目描述:有一组任务,每个任务有一个开始时间和结束时间以及需要花费的时间,同一时间只能完成一个任务。现在需要尽可能多地完成任务,求最多可以完成多少个任务。 解法:对于每个任务,按照结束时间从小到大排序,依次遍历每个任务,如果当前任务的开始时间大于等于上一个任务的结束时间,则可以完成,否则不能完成。采用贪心思想,每次选择结束时间最早的任务,可以得到最多可以完成的任务数量。 Java代码: ```java public int maxTasks(int[][] tasks) { if (tasks == null || tasks.length == 0) { return 0; } Arrays.sort(tasks, (a, b) -> a[1] - b[1]); int count = 1; int end = tasks[0][1]; for (int i = 1; i < tasks.length; i++) { if (tasks[i][0] >= end) { count++; end = tasks[i][1]; } } return count; } ``` 3. 目描述:有一组会议,每个会议有一个开始时间和结束时间,同一时间只能参加一个会议。现在需要在有限的时间内参加尽可能多的会议,求最多可以参加多少个会议。 解法:对于每个会议,按照结束时间从小到大排序,依次遍历每个会议,如果当前会议的开始时间大于等于上一个会议的结束时间,则可以参加,否则不能参加。采用贪心思想,每次选择结束时间最早的会议,可以得到最多可以参加的会议数量。 Java代码: ```java public int maxMeetings(int[] start, int[] end, int limit) { if (start == null || start.length == 0 || end == null || end.length == 0 || start.length != end.length) { return 0; } int[][] meetings = new int[start.length][2]; for (int i = 0; i < start.length; i++) { meetings[i][0] = start[i]; meetings[i][1] = end[i]; } Arrays.sort(meetings, (a, b) -> a[1] - b[1]); int count = 0; int time = 0; for (int i = 0; i < meetings.length; i++) { if (meetings[i][0] >= time && time + (meetings[i][1] - meetings[i][0]) <= limit) { count++; time += meetings[i][1] - meetings[i][0]; } } return count; } ``` 以上是三个贪心算法目和Java代码实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值