1. 程式人生 > 實用技巧 >Solution -「LOCAL」逃生

Solution -「LOCAL」逃生

\(\mathcal{Description}\)

  有 \(n\) 個人掉進了深度為 \(h\) 的坑裡,第 \(i\) 個人的肩高為 \(a_i\),臂長為 \(b_i\)。設當前坑裡人的集合為 \(S\),第 \(i\) 人能逃生,當且僅當 \(\sum_{j\in S}a_j+b_i\ge h\)。求最多逃生人數。

  \(n\le2\times10^5\)

\(\mathcal{Solution}\)

  考慮在最優情況下,相鄰兩個逃生的人,設其肩高臂長分別為 \((a,b),(p,q)\),未逃生者肩高之和 \(s\),則現在有:

\[\begin{cases} s+b\ge h\\ s-a+q\ge h \end{cases} \]

  嘗試交換兩人順序:

\[\begin{cases} s+q\ge h\\ s-p+b\ge h \end{cases} \]

  不難發現 \(b-p\le q-a\) 時,前式可推出後時,可結合題意理解為“前者逃生,考慮為後者手變短,那麼後者手越長越優”。依此排序。

  此後,順序掃一遍,若當前人能逃生直接逃生。但為保證最優,我們需要最大化坑裡人的 \(\sum a_i\)。這時考慮一個反悔操作——用逃生的一個人來替換當前這個人。顯然由於貪心的排序,替換一定成立,只要逃生的人的肩高大於當前人,替換就會優化答案,所以直接抓肩高最大的人回坑裡就好。

\(\mathcal{Code}\)

#include <queue>
#include <cstdio>
#include <algorithm> 

typedef long long LL;

inline int rint () {
	int x = 0; char s = getchar ();
	for ( ; s < '0' || '9' < s; s = getchar () );
	for ( ; '0' <= s && s <= '9'; s = getchar () ) x = x * 10 + ( s ^ '0' );
	return x;
}

const int MAXN = 2e5;
int n, H;
LL sum;
std::priority_queue<int> heap;

struct Person {
	int a, b;
	inline void read () { a = rint (), b = rint (); }
	inline bool operator < ( const Person t ) const { return b - t.a < t.b - a; }
} per[MAXN + 5];

int main () {
	freopen ( "escape.in", "r", stdin );
	freopen ( "escape.out", "w", stdout );
	n = rint ();
	for ( int i = 1; i <= n; ++ i ) per[i].read (), sum += per[i].a;
	H = rint ();
	int ans = 0;
	std::sort ( per + 1, per + n + 1 );
	for ( int i = 1; i <= n; ++ i ) {
		heap.push ( per[i].a );
		if ( sum + per[i].b >= H ) ++ ans;
		else sum += heap.top (), heap.pop ();
		sum -= per[i].a;
	}
	printf ( "%d\n", ans );
	return 0;
}

\(\mathcal{Details}\)

  也是夠勇的,不拍就過了這道智慧貪心題 owo!