Difficult Mountain(贪心/思维/好题)

题目
题意:给定一座山,高度为d;给定n个人,跳高能力为 s i s_i si,当第i个人跳过山后,山的高度会变成 d = m a x ( d , a i ) d=max(d,a_i) d=max(d,ai);一个人能跳过山,当前仅当他的 s i > = d s_i>=d si>=d。问怎么排这n个人的过山顺序,使得能过山的人最多。
官方题解
2700的贪心,我愿称之为最强贪心。(开始翻译题解QAQ)

  • 显然,所有 s i < d s_i<d si<d,可以不考虑。

  • 定义序列 p 1 , p 2 , . . . , p k p_1,p_2,...,p_k p1,p2,...,pk,为完美序列,如果对于每个 i i i,有 m a x ( d , a p 1 , . . . , a p i − 1 ) < = s p i max(d,a_{p_1},...,a_{p_{i-1}})<=s_{p_i} max(d,ap1,...,api1)<=spi

  • 对于 a i < = s i a_i<=s_i ai<=si的所有集合,显然是可以构成完美序列。只要将他们按 s i s_i si升序排序即可。

  • 对于 a i > s i a_i>s_i ai>si的集合,可以通过贪心获取最大的完美序列集合。按 a i a_i ai排序,每次遇到 s i > = d s_i>=d si>=d,则更新 d = a i d=a_i d=ai,同时 n u m + 1 num+1 num+1。(因为这样保证每次更新d都是取了当前最小的代价 a i a_i ai

  • 考虑点对 i , j i,j i,j,满足 s i < a j < = s j < a i s_i<a_j<=s_j<a_i si<aj<=sj<ai。显然这两个点是冲突的,不能相容于一个完美序列。对于一个可能的完美序列,如果它含有i,那它可以被j代替。 c u r d < s i < s j → s j > c u r d cur_d<s_i<s_j \rightarrow s_j>cur_d curd<si<sjsj>curd; a j < a i < n x t s → a j < n x t s a_j<a_i<nxt_s \rightarrow a_j<nxt_s aj<ai<nxtsaj<nxts

我们可以预处理,将数据分为 s i > = a i ( p 数 组 ) s_i>=a_i (p数组) si>=ai(p) s i < a i ( q 数 组 ) s_i<a_i(q数组) si<ai(q)。双指针,遍历p和q,定义集合 M M M(按 a i a_i ai降序)。
w h i l e ( q j . s < p i . a ) M . i n s e r t ( c j ) ; while (q_j.s <p_i.a) M.insert(c_j); while(qj.s<pi.a)M.insert(cj);
w h i l e ( M 1 . a > p i . s ) M . e r a s e ( M 1 ) while (M_1.a>p_i.s) M.erase(M_1) while(M1.a>pi.s)M.erase(M1)
通过以上步骤,不在M集合的q数据,则和p数据是冲突的,而在M集合的p数据,不会和q数据冲突。
那么怎么合并p和M数据呢?可以将数据按 ( m a x ( a i , s i ) , s i ) (max(a_i,s_i),s_i) (max(ai,si),si)升序排序。
通过这种排序方式,p数据是按 s i s_i si升序,M数据则处于p数据之间,即 p i − 1 . s < ( M j . a = m a x ( M j . a , M j . s ) ) < p i . s p_{i-1}.s<(M_j.a=max(M_j.a,M_j.s))<p_i.s pi1.s<(Mj.a=max(Mj.a,Mj.s))<pi.s,同时M数据是按 a j a_j aj升序。
排好序后,根据上述第4点的贪心规则来取数,就可以达到最大值了。

跟着上述思路码了一波。

#include <bits/stdc++.h> 
using namespace std;
const int maxn = 500010;

int n, d;
int s[maxn], a[maxn];
int np, nq, nr;
struct node {
	int s, a;
	bool operator < (const node &x) const {
		return a > x.a;
	}
}p[maxn], q[maxn], r[maxn];

bool cmp(const node &x, const node &y) {
	return x.a < y.a;
}

bool cmp2(const node &x, const node &y) {
	return x.s < y.s;
}

bool cmp3(const node &x, const node &y) {
	if (max(x.s, x.a) == max(y.s, y.a)) {
		return x.s < y.s;
	}
	return max(x.s, x.a) < max(y.s, y.a);
}

void debug() {
	printf("np %d\n", np);
	for (int i = 0; i < np; ++i) {
		printf("(%d, %d) ", p[i].s, p[i].a);
	}
	printf("\nnq %d\n", nq);
	for (int i = 0; i < nq; ++i) {
		printf("(%d, %d) ", q[i].s, q[i].a);
	}
	printf("\nnr %d\n", nr);
	for (int i = 0; i < nr; ++i) {
		printf("(%d, %d) ", r[i].s, r[i].a);
	}
	printf("\n");
}

void pq_init() {
	np = 0, nq = 0;
	int k = 0;
	for (int i = 0; i < n; ++i) {
		if (s[i] < d) continue;// remove all s[i] < d
		if (s[i] >= a[i]) {
			p[np].a = a[i];
			p[np].s = s[i];
			++np;
		} else {
			q[nq].a = a[i];
			q[nq].s = s[i];
			++nq;
		}
		++k;
	}
	n = k;
	sort(p, p + np, cmp);
	sort(q, q + nq, cmp2);
	nr = 0;
	for (int i = 0; i < np; ++i) {
		r[nr++] = p[i];
	}
}

int main() {
	scanf("%d%d", &n, &d);
	for (int i = 0; i < n; ++i) {
		scanf("%d%d", &s[i], &a[i]);
	}
	
	pq_init();
	
	
	multiset<node> st;
	
	int j = 0;
	for (int i = 0; i < np; ++i) {
		while (j < nq && q[j].s < p[i].a) {
			st.insert(q[j]);
			++j;
		}
		while (!st.empty() && p[i].s < (*(st.begin())).a) {
			st.erase(st.begin());
		}
		while (!st.empty()) {
			r[nr++] = *st.begin();
			st.erase(st.begin());
		}
	}
	while (j < nq) {
		r[nr++] = q[j];
		++j;
	}
//	if (!np) {
//		nr = nq;
//		for (int i = 0; i < nq; ++i) {
//			r[i] = q[i];
//		}
//	}
	
	int ans = 0;
	sort(r, r + nr, cmp3);
//	debug();
	for (int i = 0; i < nr; ++i) {
		if (d <= r[i].s) {
			++ans;
			d = max(d, r[i].a);
		}
	}
	
	printf("%d\n", ans);
}

之后看别人题解,加分析,发现,可以更简单。。贪心yyds啊

#include <bits/stdc++.h> 
using namespace std;
const int maxn = 500010;

int n, d;

int nr;
struct node {
	int s, a;
}r[maxn];

bool cmp(const node &x, const node &y) {
	if (max(x.s, x.a) == max(y.s, y.a)) {
		return x.s < y.s;
	}
	return max(x.s, x.a) < max(y.s, y.a);
}


int main() {
	scanf("%d%d", &n, &d);
	for (int i = 0; i < n; ++i) {
		scanf("%d%d", &r[i].s, &r[i].a);
	}
	
	int ans = 0;
	sort(r, r + n, cmp);
	for (int i = 0; i < n; ++i) {
		if (d <= r[i].s) {
			++ans;
			d = max(d, r[i].a);
		}
	}
	
	printf("%d\n", ans);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值