洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法

洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法

填表法 :就是一般的动态规划,当前点的状态,可以直接用状态方程,根据之前点的状态推导出来。

刷表法:由当前点的状态,更新其他点的状态。需要注意:只用当每个状态所依赖的状态对它的影响相互独立。

下面会用一道题目给出两种写法的区别


题目链接:P1156 垃圾陷阱

题意吃垃圾就完了

卡门――农夫约翰极其珍视的一条Holsteins奶牛――已经落了到“垃圾井”中。“垃圾井”是农夫们扔垃圾的地方,它的深度为 D ( 2 ≤ D ≤ 100 ) D(2 \le D \le 100) D(2D100)英尺。

卡门想把垃圾堆起来,等到堆得与井同样高时,她就能逃出井外了。另外,卡门可以通过吃一些垃圾来维持自己的生命。

每个垃圾都可以用来吃或堆放,并且堆放垃圾不用花费卡门的时间。

假设卡门预先知道了每个垃圾扔下的时间 t ( 0 < t ≤ 1000 ) t(0< t \le 1000) t(0<t1000),以及每个垃圾堆放的高度 h ( 1 ≤ h ≤ 25 h(1 \le h \le 25 h(1h25)和吃进该垃圾能维持生命的时间 f ( 1 ≤ f ≤ 30 ) f(1 \le f \le 30) f(1f30),要求出卡门最早能逃出井外的时间,假设卡门当前体内有足够持续 10 10 10小时的能量,如果卡门 10 10 10小时内没有进食,卡门就将饿死。

注:如果能量为 0 0 0 时恰好有垃圾可以吃,则不会饿死

注意到对于每个垃圾,只有吃掉和堆起来两种方法

d p [ i ] [ j ] dp[i][j] dp[i][j] 表示只考虑前 i i i 个垃圾,堆到高度 j j j 时,最多可以活到什么时候

容易推出方程
d p [ i ] [ j ] = max ⁡ ( d p [ i − 1 ] [ j ] + v i , d p [ i − 1 ] [ j − h i ] ) dp[i][j]=\max(dp[i-1][j]+v_i,dp[i-1][j-h_i]) dp[i][j]=max(dp[i1][j]+vi,dp[i1][jhi])
1.填表法

求出dp数组以后扫一遍就好了,细节较多(其实好像可以滚动数组,但是懒 qwq)

注意这里垃圾的高度可能会超过 m m m ,但是数据好像没卡这个东西

处理方法就是把 m + max ⁡ ( h i ) m+\max(h_i) m+max(hi) 都dp一下

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(1e3+15)

struct node
{
    int h,v,t;
}a[N];
int n,m,dp[N][N];
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    memset(dp,0xc0,sizeof(dp));
    dp[0][0]=10;
    cin >> m >> n;
    for(int i=1; i<=n; i++)
        cin >> a[i].t >> a[i].v >> a[i].h;
    sort(a+1,a+1+n,[](node a,node b){return a.t<b.t;});
    for(int i=1; i<=n; i++)
        for(int j=0; j<=m+25; j++)
        {
            if(dp[i-1][j]>=a[i].t)
                dp[i][j]=max(dp[i][j],dp[i-1][j]+a[i].v);
            if(j>=a[i].h&&dp[i-1][j-a[i].h]>=a[i].t)
                dp[i][j]=max(dp[i][j],dp[i-1][j-a[i].h]);
        }
    int res1=-1,res2=-1;
    for(int i=1; i<=n; i++)
    {
        for(int j=0; j<=m+25; j++)
        {
            if(dp[i][j]>=a[i].t)
                res1=max(res1,j);
            res2=max(res2,dp[i][j]);
        }
        if(res1>=m){cout << a[i].t << endl;return 0;}
    }
    cout << res2 << endl;
    return 0;
}

2.刷表法

这里可以滚动数组了,不过要两个

否则会导致数据更新错误

当然也可以不用滚动数组 位运算那么可爱为什么不写她呐?对不对?

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(1e3+15)

struct node
{
    int h,v,t;
}a[N];
int n,m,dp[2][N];
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    memset(dp,0xc0,sizeof(dp));
    dp[0][0]=10;
    cin >> m >> n;
    for(int i=1; i<=n; i++)
        cin >> a[i].t >> a[i].v >> a[i].h;
    sort(a+1,a+1+n,[](node a,node b){return a.t<b.t;});
    int res=-1;
    for(int i=1; i<=n; i++)
    {
        int cur=i&1,pre=cur^1;
        memset(dp[cur],0xc0,sizeof(dp[cur]));
        for(int j=m; j>=0; j--) // 这里就不用m+25了,因为只要超出了直接就输出了
        {
            if(dp[pre][j]<a[i].t)continue;
            if(j+a[i].h>=m)return cout << a[i].t << endl,0;
            dp[cur][j+a[i].h]=max(dp[cur][j+a[i].h],dp[pre][j]);
            dp[cur][j]=max(dp[cur][j],dp[pre][j]+a[i].v);
        }
        res=max(res,dp[cur][0]);
    }
    cout << res << endl;
    return 0;
}

哼哼,我才不会说我是在考数学的时候推出来的dp方程

转载请说明出处

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值