卧薪尝胆,厚积薄发。
TJOI2013 拯救小矮人
Date: Sun Oct 21 11:11:01 CST 2018 In Category: NoCategory

Description:

有 $n$ 个人,每个人有肩高和臂长,每个人可以踩在另一个人的肩上,想逃出高位 $H$ 的坑,问最多能有多少人出去。
$1\leqslant n\leqslant 2000$

Solution:

设肩高为 $A_i$ ,臂长为 $B_i$ ,首先证明一个结论,就是跳出坑的顺序一定是按 $A_i+B_i$ 从小到大的,证明一下:
设剩下的肩高的和为 $SA$ ,那么 $x$ 能在 $y$ 之前跳出去当且仅当满足 $SA+A_y+A_x+B_x\geqslant H$ 且 $SA+A_y+B_y\geqslant H$ ,也就是 $SA\geqslant H-A_y-\min(A_x+B_x,B_y)$ ,那么当 $H-\min(A_x+B_x+A_y,A_y+B_y)\leqslant H-\min(A_y+B_y+A_x,A_x+B_x)$ 时, $x$ 在 $y$ 之前出去需要的 $SA$ 更小,也就更有可能都出去,用国王游戏的思想整理一下发现就是 $A_x+B_x\leqslant A_y+B_y$ 。
已知出去的顺序,那么就可以 $DP$ 了,具体的做法就是类似背包的做法,设 $f[i][j]$ 表示前 $i$ 个人跳出去了 $j$ 个最高能堆多高,转移为:
$f[i][j]=\max(f[i-1][j-1](f[i-1][j-1]+A_i+B_i+sufh[i+1]\geqslant H),f[i-1][j]+A_i)$
因为要按顺序跳,所以后面的一定都还没跳,因此记一个后缀和 $sufh$ 不会有问题。
总结一下这道题就是主要思想是 $DP$ ,但是直接 $DP$ 正确性没保证(因为 $sufh$ 没保证),所以先贪心一下。

Code:


#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cctype>
#include<cstring>
using namespace std;
int n,h;
#define MAXN 2010
struct dwarf
{
int a,b;
}s[MAXN];
bool cmp(dwarf x,dwarf y){return x.a + x.b < y.a + y.b;}
int f[MAXN][MAXN];
int sufh[MAXN];
int main()
{
scanf("%d",&n);
for(int i = 1;i <= n;++i)
{
scanf("%d%d",&s[i].a,&s[i].b);
}
scanf("%d",&h);
sort(s + 1,s + 1 + n,cmp);
for(int i = n;i >= 1;--i)sufh[i] = sufh[i + 1] + s[i].a;
memset(f,0xc0,sizeof(f));
f[0][0] = 0;
for(int i = 1;i <= n;++i)
{
for(int j = i;j >= 0;--j)
{
f[i][j] = f[i - 1][j] + s[i].a;
if(j >= 1 && f[i - 1][j - 1] + s[i].a + s[i].b + sufh[i + 1] >= h)
{
f[i][j] = max(f[i][j],f[i - 1][j - 1]);
}
}
}
for(int i = n;i >= 0;--i)
{
if(f[n][i] >= 0)
{
cout << i << endl;
break;
}
}
return 0;
}
Copyright © 2020 wjh15101051
ღゝ◡╹)ノ♡