BZOJ 3174 拯救小矮人(贪心+DP)

题意

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

思路

已知两个相邻的小矮人\(a\)\(b\),考虑交换\(ab\)位置的代价
首先如果\(a\)\(b\)下面,那么第一次的高度是\(a.a+b.a+b.b\),第二次的高度是\(a.a+a.b\)
然后如果\(b\)\(a\)下面,那么第一次的高度是\(a.a+b.a+a.b\),第二次的高度是\(b.a+b.b\)
我们要考虑的就是交换ab会对原来的答案有什么影响
比较直观的想法是关键看第二次的高度,因为第二次的高度更高的那个,最终一定比另一个优。
还要考虑可能会出现的第二个比另一个更高但是第一个不如另一个的情况。
即可能出现\(a.a+a.b>b.a+b.b\),但是\(a.a+b.a+b.b<a.a+b.a+a.b\),即\(a.b>a.a+b.b\)。要知道这是完全可能的
考虑在这种情况下,假设\(a.b>a.a+b.b\),那不交换的情况下第一次是\(a.a+b.a+b.b<a.b+b.a<b.b+a.b+a.a\)因此交换完还是更优
所以只需\(a.a+a.b<a.a+b.b\)即可
上面我们已经证明按贪心完的顺序取是最优的,但是还要确定最多能走掉多少个
接下来要\(dp\):令\(f[i]\)表示走完i个矮人之后还能取到的最大高度
这样贪心的作用就出来了:按照贪心完的顺序取走矮人,可以保证最优。
一开始初始化\(f[0]\)表示没有矮人走掉,\(f[0]=Σa[i]\)
然后就是枚举取到第i个矮人,可以用它来更新\(f[j]\)的情况是\(f[j]+ai.b>=m\)
这样就可以算出最大值了

代码

# include<bits/stdc++.h>
using namespace std;
# define lowbit(x) ((x)&(-x))
# define pi acos(-1.0)
# define eps 1e-8
# define MOD 100000007
# define INF 1000000000
# define mem(a,b) memset(a,b,sizeof(a))
# define FOR(i,a,n) for(register int i=a; i<=n; ++i)
# define FDR(i,a,n) for(register int i=a; i>=n; --i)
# define bug puts("H");
# define lch p<<1,l,mid
# define rch p<<1|1,mid+1,r
# define mp make_pair
# define pb push_back
typedef pair<int,int> PII;
typedef vector<int> VI;
# pragma comment(linker, "/STACK:1024000000,1024000000")
typedef long long LL;
inline char nc(){
    static char buf[1000000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline int Scan(){
    char ch=nc();int sum=0, f=1;
    if (ch=='-') f=-1, ch=nc();
    while(!(ch>='0'&&ch<='9'))ch=nc();
    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
    return sum*f;
}
const int N=2005;
//Code begin....

struct Node{int x, y;}node[N];
int dp[N];

bool comp(Node a, Node b){return a.x+a.y<b.x+b.y;}
int main ()
{
    int n, ans, H;
    scanf("%d",&n);
    mem(dp,-1); dp[0]=0;
    FOR(i,1,n) scanf("%d%d",&node[i].x,&node[i].y), dp[0]+=node[i].x;
    scanf("%d",&H);
    sort(node+1,node+n+1,comp);
    FOR(i,1,n) FDR(j,i,1) if (dp[j-1]+node[i].y>=H) dp[j]=max(dp[j],dp[j-1]-node[i].x);
    FDR(i,n,0) if (dp[i]!=-1) {ans=i; break;}
    printf("%d\n",ans);
    return 0;
}
posted @ 2017-09-15 17:37  free-loop  阅读(2290)  评论(0编辑  收藏  举报