[bzoj3174][Tjoi2013]拯救小矮人
DP+贪心
前言
说实话,我感觉网上大多数文章讲这篇都是在口胡,我发现znber同学的证明也是明显错误的(也许是我太蒻了).但有幸的是我遇上了这篇文章http://blog.csdn.net/commonc/article/details/51693992
他跨了一年,做出了这道题,心疼QAQ。但我觉得他这篇还是没有多详细,搜索引擎也不容易先搜到他,于是就写了这篇辣鸡题解QAQ。
题意
我一开始一直纠结于“一个小矮人走后剩下的小矮人能不能重新组成梯子”。后面发现如果你把确定出去的小矮人直接依次排列,就可以不用重新组成了。所以这是一个没有意义的问题QAQ.所以我们现在的问题是,确定一个排列,
使得最后的能够出去的人最多(根据我刚才所说,能出去的人一定排在最后)(我这里最后代表着梯子的最上面).
求解第一步.贪心
我们把最后的那一部分(即要出去的人)拿出来看,发现他们的这个序列的ai+bi是单调递减的.具体证明如下:
如果有Ai与Aj相邻,且Ai在Aj的前面,Ai+Bi < Aj + Bj,此时的序列合法,那么我们需要证明把它们交换后此时的序列仍然合法.假设Ai前面的Ax的和是Sum.那么我们先看Ai,Ai+Sum+Bi是大于井深H的,我们交换后,Ai手伸出的高度变为Ai+Sum+Bi+Aj>Ai+Sum+Bi>H.显然依然合法.然后我们看Aj,他出去时高度变为Aj+Bj+Sum>Ai+Bi+Sum>H.也是合法的。
求解第二步.Dp
我们把原来序列从小到大排了以后,显然就能进行Dp了,我们可以注意到,对于一个矮人来说,如果他要被选的话,除了前面选了的矮人。其他所有的矮人都要被他踩在脚底,显然其他所有矮人高度最矮为好,这样他就能够踩得更高。那我们f[i][j]表示前i个位置选j个矮人,这j个矮人的最小高度和.然后就很好转移辣。还可以注意的是,这玩意儿是可以省掉前面一维的QAQ
Code
#include<cstdio>
#include<algorithm>
#include<cstring>
const int N = 2e3 + 7, INF = 0x3f3f3f3f;
char B[1 << 12], *S = B, *T = B;
char getchar2 () { return S == T && (T = (S = B) + fread (B, 1, 1 << 12, stdin), S == T) ? -1 : *S++; }
char pr[1 << 12], *pt = pr;
void putchar2 (char x) { if (pt - pr == 1 << 12) fwrite (pr, 1, 1 << 12, stdout), pt = pr; *pt++ = x; }
void G (int &num) {
static char a;
for (a = getchar2 (); a > '9' || a < '0'; a = getchar2 ()) ;
for (num = 0; a >= '0' && a <= '9'; a = getchar2 ()) num = (num << 3) + (num << 1) + a - '0';
}
void print (int x) {
static char stk[12]; static int top;
if (!x) { putchar2 ('0'); putchar2 ('\n'); return ; }
while (x) stk[++top] = x % 10 + '0', x /= 10;
while (top) putchar2 (stk[top--]); putchar2 ('\n');
}
void IO () {
freopen ("3174.in", "r", stdin);
freopen ("3174.out", "w", stdout);
}
int n, f[N], h, sum;
struct item {
int x, y;
void init () { G (x); G (y); sum += x; }
bool operator < (const item &rhs) const { return x + y < rhs.x + rhs.y; }
}s[N];
int main () {
// IO ();
memset (f, 0x3f, sizeof (f));
G (n); for (int i = 1; i <= n; ++i) s[i].init (); G (h);
std :: sort (s + 1, s + n + 1); f[0] = 0;
for (int i = 1; i <= n; ++i) {
for (int j = i; j; --j) {
if (sum - f[j - 1] + s[i].y >= h) f[j] = std :: min (f[j], f[j - 1] + s[i].x);
}
}
for (int i = n; ~i; --i) if (f[i] != INF) {
printf ("%d\n", i);
return 0;
}
}