[TJOI 2013]拯救小矮人

Description

一群小矮人掉进了一个很深的陷阱里,由于太矮爬不上来,于是他们决定搭一个人梯。即:一个小矮人站在另一小矮人的 肩膀上,知道最顶端的小矮人伸直胳膊可以碰到陷阱口。对于每一个小矮人,我们知道他从脚到肩膀的高度Ai,并且他的胳膊长度为Bi。陷阱深度为H。如果我 们利用矮人1,矮人2,矮人3,。。。矮人k搭一个梯子,满足A1+A2+A3+....+Ak+Bk>=H,那么矮人k就可以离开陷阱逃跑了,一 旦一个矮人逃跑了,他就不能再搭人梯了。
我们希望尽可能多的小矮人逃跑, 问最多可以使多少个小矮人逃跑。

Input

第一行一个整数N, 表示矮人的个数,接下来N行每一行两个整数Ai和Bi,最后一行是H。(Ai,Bi,H<=10^5)

Output

一个整数表示对多可以逃跑多少小矮人

Sample Input1

2
20 10
5 5
30

Sample Output1

2

Sample Input2

2
20 10
5 5
35

Sample Output2

1

HINT

数据范围
30%的数据 N<=200
100%的数据 N<=2000

题解

这道题话说网上很多题解都是错的耶...

基本上都是用“身高+臂长”作为逃生能力来进行解释...

但是如果按照普遍的题解,我们无法解释在$DP$的过程中“第$i$个人不选的情况”...

打个比方,我举出一组反例:

3

5 1

1 6

4 4

12

按普遍的题解来说我们按所谓的“逃生能力”排序,为$5+1$,$1+6$,$4+4$。

那么我们按“逃生顺序”来,我们一个都不能逃出(第一个人就是$1+5+1+4<12$)。然而若我们让最后一个人先走($5+1+4+4>12$)显然能逃出。

显然这个顺序并不是所谓的“逃生顺序”,那要怎么做?

首先我们还是是要按照身高+臂长来排序,但是为什么呢?

大概证明如下:

考虑对于最上面的两个人,下面的人梯高度一定

如果无论怎样都出不去,那相对顺序肯定是无所谓的

如果怎么都能出去,那相对顺序肯定也是无所谓的

关键就是剩下的情况,两个人都有机会逃出去,但是排的先后顺序会影响逃跑结果

这种情况下就只能让身高+臂长比较小的人较先离开,按照刚才的说法,因为他的逃生能力比较弱

但是对吗?

随之而来的又有一个问题,只贪心到底行不行,而刚刚举出的反例显然说明了这个问题

这怎么办呢?

这时我们就发现“这种情况下就只能让身高+臂长比较小的人较先离开,因为他的逃生能力比较弱”这句话是错的

但是它为我们提供了一个思路,那就是最终的最优逃出方案一定可以是一个按照“身高+臂长”递增的序列

这个怎么证呢?就是当我们发生上述冲突时,我们肯定是选择让高个的留下并且永远留下,矮个那个先走,这样就满足了性质

然后就可以$DP$了!

所以!!我们排序+$DP$的原因不是XX放在XX前面一定更优,而是最终的逃跑序列一定是一个“身高+臂长”递增的序列,为了方便$DP$,所以我们才要排序!!!!

(部分题解源commonc的博客

 1 //It is made by Awson on 2017.9.27
 2 #include <set>
 3 #include <map>
 4 #include <cmath>
 5 #include <ctime> 
 6 #include <queue>
 7 #include <stack>
 8 #include <string>
 9 #include <cstdio>
10 #include <vector>
11 #include <cstdlib>
12 #include <cstring>
13 #include <iostream>
14 #include <algorithm>
15 #define Min(a, b) ((a) < (b) ? (a) : (b))
16 #define Max(a, b) ((a) > (b) ? (a) : (b))
17 #define LL long long
18 using namespace std;
19 const int N = 2000;
20 void read(int &x) {
21     char ch; bool flag = 0;
22     for (ch = getchar(); !isdigit(ch) && ((flag |= (ch == '-')) || 1); ch = getchar());
23     for (x = 0; isdigit(ch); x = (x<<1)+(x<<3)+ch-48, ch = getchar());
24     x *= 1-2*flag;
25 }
26 
27 struct tt {
28     int a, b;
29     bool operator < (const tt &q) const{
30         return a+b < q.a+q.b;    
31     }
32 }a[N+5];
33 int n, h;
34 int f[N+5];
35 
36 void work() {
37     read(n);
38     for (int i = 1; i <= n; i++)
39         read(a[i].a), read(a[i].b);
40     sort(a+1, a+n+1);
41     read(h);
42     memset(f, -1, sizeof(f));
43     int ans = 0;
44     f[0] = 0;
45     for (int i = 1; i <= n; i++) f[0] += a[i].a;
46     for (int i = 1; i <= n; i++) {
47         for (int j = ans; j >= 0; j--) {
48             if (f[j]+a[i].b >= h)
49                 f[j+1] = Max(f[j+1], f[j]-a[i].a);
50             if (f[ans+1] >= 0) ans++;
51         }
52     }
53     printf("%d
", ans);
54 }
55 int main() {
56     work();
57     return 0;
58 }
原文地址:https://www.cnblogs.com/NaVi-Awson/p/7604324.html