[題解]luogu P1156 垃圾陷阱

前言:[數據刪除]

來源:題解


 

不發題面了

首先我们来分析题目,“每个垃圾都可以用来吃或堆放”,浓浓的透露出一个背包气息。我们可以类比背包问题的放或不放。于是dp[i][j]dp[i][j]dp[i][j]描述为处理前i个物品的某状态j,那么状态j代表什么呢?

我们可以继续分析题目并使用排除法

分析题目,我们需要求的答案是时间,于是很自然而然的想到j描述高度或生命,而dp数组存放时间。很显然,这样状态既不完整,也写不出转移方程。而且dp数组存的是当前状态下最大或最小的价值,似乎也不满足。

这时候我们发现,有4个值可能成为状态,高度,生命,物品和时间,难道要dp三维的吗?

再分析题目,每个垃圾都有一个下落的时间,奶牛一定是在垃圾丢下来的时间就处理垃圾的(可以得出这样的最优的),那么物品就可以和时间关联起来了。这时候,我们可以把时间仅仅当作干扰量给剔除了。

需要注意的是,物品的使用顺序并不是随意的,必须按它们下落的时间顺序来先后处理。(这里排一下序即可)

那么j代表什么呢?

一下子我们并不能得出答案。先尝试dp[i][j]代表前i件物品处理后在j血量时达到的最大高度。

值得一提的是,j血量表示奶牛在暂时不考虑时间时所得到的最大血量

据说这个是叫离线

试着写一下它的状态转移方程

dp[i][j]=max(dp[i−1][j]+trash[i].h,dp[i−1][j+trash[i].c])

发现这是对的,然而我们再想想,在关于j的一重循环里面,对j的取值我们似乎并不好判断,甚至要枚举很大。

所以我们再尝试讨论dp[i][j]代表前i件物品处理后在h高度时达到的最大血量。

状态转移

dp[i][j]=max(dp[i−1][j]+trash[i].c,dp[i−1][j−trash[i].h])

发现这样也是对的,而且j枚举起来也比较方便,于是我们选择这种算法。

重要内容:分析 i,j 代表什么:

按時間順序排序然後依次轉移,時間數值不固定或者比較大不太好做階段,而且會有很多浪費,所以用前 i 個物品轉移,轉移時在判可行性,處理掉了時間,

只剩 血量,高度兩維,在考慮怎麼分配,如果用血量做第二維的話數值仍然不確定而且可能很大,於是嘗試交換,對於能否活下來在最後找解的時候在判斷。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int d,g;
struct node{
    int t,h,f;
}e[101];
int f[101][1001];//i個物品j高度的最大血量(重要 
bool cmp(node a,node b)
{
    return a.t<b.t;
}
int main()
{
    scanf("%d%d",&d,&g);
    for(int i=1;i<=g;i++){
        scanf("%d%d%d",&e[i].t,&e[i].f,&e[i].h);
    }
    sort(e+1,e+1+g,cmp);
    f[0][0]=10;//初值 
    for(int i=1;i<=g;i++)
    for(int j=0;j<=d;j++){
        if(f[i-1][j]>=e[i].t)//如果上一個狀態的血量夠用 
        f[i][j]=max(f[i][j],f[i-1][j]+e[i].f);//
        if(j>=e[i].h&&f[i-1][j-e[i].h]>=e[i].t)//如果可以從前一個更低的高度加上這個物品的高度,並且能堅持到下一個垃圾
        f[i][j]=max(f[i][j],f[i-1][j-e[i].h]);//
    }
    int maxh=0,maxt=0,i;
    for(i=1;i<=g;i++){
        for(int j=0;j<=d;j++){
            if(f[i][j]-e[i].t>=0)//一定要判狀態能否使用 
            maxh=max(maxh,j);//更新最高高度 
            maxt=max(maxt,f[i][j]);//每次都要更新最長時間 
        }
        if(maxh>=d)break;
    }
    if(maxh>=d)printf("%d",e[i].t);
    else printf("%d",maxt);
}

 

posted @ 2019-03-12 20:44  羊肉汤泡煎饼  阅读(147)  评论(0编辑  收藏  举报