luogu1156垃圾陷阱题解--背包DP

题目链接

https://www.luogu.org/problemnew/show/P1156

方法1

分析

将已经爬的高度看作背包容积,最大剩余血量看作价值,\(f[i][j]\)表示吃完第\(i\)个垃圾,爬到\(j\)高度的最大剩余血量

\(f[i][j+h[i]]=max(f[i][j+h[i]],f[i-1][j]-(t[i]-t[i-1]))\)

\(f[i][j]=max(f[i][j],f[i-1][j]+c[i]-(t[i]-t[i-1]))\)

当然还要判断能否撑到下一个垃圾的到来,以及判断是否已经爬出了陷阱

如果不能爬出,显然将垃圾全部吃完是最优的,因为垃圾全用来吃了并且没撑到下一个时刻,所以\(ans=max(ans,f[i][0]+t[i])\)

代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <cstdlib>
#include <iostream>
#define ll long long 
#define ri register int 
using std::min;
using std::max;
template <class T>inline void read(T &x){
    x=0;int ne=0;char c;
    while(!isdigit(c=getchar()))ne=c=='-';
    x=c-48;
    while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48;
    x=ne?-x:x;return ;
}
const int maxn=1005;
const int inf=0x7fffffff;
int f[maxn][105];
bool e[maxn];
int l,m,n;
struct Tra{
    int t,c,h;
    bool operator <(const Tra &b)const{
        return t==b.t?c>b.c:t<b.t;
    }
}tra[maxn];
int main(){
    int dta,h,ans=10;
    read(l),read(n);
    for(ri i=1;i<=n;i++){
        read(tra[i].t),read(tra[i].c),read(tra[i].h);
    }
    std::sort(tra+1,tra+1+n);
    memset(f,-1,sizeof(f));
    f[0][0]=10;
    for(ri i=1;i<=n;i++){
        h=tra[i].h;
        dta=tra[i].t-tra[i-1].t;
        for(ri j=l;j>=0;j--){
            if(f[i-1][j]==-1||f[i-1][j]<dta)continue;
            if(j+h>=l){
                printf("%d\n",tra[i].t);
                return 0;
            }
            f[i][j+h]=max(f[i-1][j]-dta,f[i][j+h]);
            f[i][j]=max(f[i-1][j]+tra[i].c-dta,f[i][j]);    
        }
        if(f[i][0]!=-1)ans=max(ans,f[i][0]+tra[i].t);
    /*注意加特判*/
    }
    printf("%d\n",ans);
    return 0;
}

方法2

分析

刚刚的方法一点也不背包,我们按着01背包套路把它变成一维的,\(f[j]\)表示爬到了高度\(j\)总共加上的最大血量.

\(f[j+h[i]]=max(f[j+h[i]],f[j])\)

\(f[j]+=c[i]\)

按照套路,为了使\(j+h[i]\)这个状态在本阶段中不再出现,我们倒序枚举高度

代码
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cctype>
#define ll long long 
#define ri register int 
using std::min;
using std::max;
template <class T>inline void read(T &x){
    x=0;int ne=0;char c;
    while(!isdigit(c=getchar()))ne=c=='-';
    x=c-48;
    while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48;
    x=ne?-x:x;return ;
}
const int maxn=1005;
const int inf=0x7ffffff;
int m,n;
struct Tra{
    int t,c,h;
    bool operator <(const Tra &b)const{
        return t<b.t;
    }
}tr[maxn];
int f[maxn];
int main(){
    read(m),read(n);
    for(ri i=1;i<=n;i++){
        read(tr[i].t),read(tr[i].c),read(tr[i].h);
    }
    std::sort(tr+1,tr+1+n);
    memset(f,-1,sizeof(f));
    f[0]=10;
    for(ri i=1;i<=n;i++){
        for(ri j=m;j>=0;j--){
            if(f[j]>=tr[i].t){
                if(j+tr[i].h>=m){
                    printf("%d\n",tr[i].t);
                    return 0;
                }
                f[j+tr[i].h]=max(f[j+tr[i].h],f[j]);
                f[j]+=tr[i].c;
            }
        }
    }
    printf("%d\n",f[0]);
    return 0;
}

转载于:https://www.cnblogs.com/Rye-Catcher/p/9628325.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值