1. 程式人生 > 實用技巧 >[Luogu] P4823 [TJOI2013]拯救小矮人

[Luogu] P4823 [TJOI2013]拯救小矮人

\(Link\)

Description

一群小矮人掉進了一個很深的陷阱裡,由於太矮爬不上來,於是他們決定搭一個人梯。即:一個小矮人站在另一小矮人的肩膀上,直到最頂端的小矮人伸直胳膊可以碰到陷阱口。

對於每一個小矮人,我們知道他從腳到肩膀的高度\(A_i\),並且他的胳膊長度為\(B_i\)。陷阱深度為\(H\)

如果我們利用矮人\(1\),矮人\(2\),矮人\(3\),...,矮人\(k\)搭一個梯子,滿足\(A_1+A_2+A_3+....+A_k+B_k>=H\),那麼矮人\(k\)就可以離開陷阱逃跑了,一 旦一個矮人逃跑了,他就不能再搭人梯了。

我們希望儘可能多的小矮人逃跑, 問最多可以使多少個小矮人逃跑。

Solution

貪心\(+DP\)

手動模擬一下就發現不能按\(A_i\)從小到大排序,肯定和\(B_i\)是有關的。

我們貪心選擇逃生能力較弱的人先逃出去(即\(A_i+B_i\)較小),這樣排序以後就不會出現後面的人先逃出去比前面的人先逃出去更優的情況。(因為如果前面的人能逃出去,把後面的人換到這個位置來一樣能逃出去。但如果後面的人先逃出去,前面的人可能會因為沒有人墊著而逃不出去了。)

做個揹包就行了,設\(dp[i]\)表示走\(i\)個人後可以取的最大高度,那麼有$。dp[1\sim{n}]=-INF,dp[0]=\sum{A_i}。 $

轉移方程為\(dp[j]=max\{dp[j-1]-A_i\}(dp[j-1]+B_i\ge{H})\)

Code

#include <bits/stdc++.h>

using namespace std;

int n, h, dp[2005];

struct node
{
	int a, b;
}p[2005];

int read()
{
	int x = 0, fl = 1; char ch = getchar();
	while (ch < '0' || ch > '9') { if (ch == '-') fl = -1; ch = getchar();}
	while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + ch - '0'; ch = getchar();}
	return x * fl;
}

int cmp(node x, node y)
{
	return x.a + x.b < y.a + y.b;
}

int main()
{
	n = read();
	for (int i = 1; i <= n; i ++ )
	{
		p[i].a = read();
		p[i].b = read();
	}
	h = read();
	sort(p + 1, p + n + 1, cmp);
	memset(dp, -0x3f, sizeof(dp));
	dp[0] = 0;
	for (int i = 1; i <= n; i ++ )
		dp[0] += p[i].a;
	for (int i = 1; i <= n; i ++ )
		for (int j = i; j >= 1; j -- )
			if (dp[j - 1] + p[i].b >= h)
				dp[j] = max(dp[j], dp[j - 1] - p[i].a);
	for (int i = n; i >= 0; i -- )
	{
		if (dp[i] >= 0)
		{
			printf("%d\n", i);
			break;
		}
	}
	return 0;
}