【洛谷1156】垃圾陷阱(动态规划)
大致题意: 一只奶牛掉进了一个垃圾陷阱里,每个垃圾有三个属性:被扔下来的时间\(T_i\),吃了能够延长的生命时间\(F_i\),叠起来的高度\(H_i\)。每一个垃圾可以用来吃或叠,如果某一时刻垃圾叠起来的总高度大于等于\(M\),奶牛就可以离开这个陷阱。已知奶牛一开始能够存活10个单位时间,问你它离开陷阱最少所需的时间。如果它无论如何都无法离开陷阱,就输出它最多能够存活的时间。
第一种情况:求离开陷阱最少所需的时间
这题应该比较显然是一道\(DP\)题。
首先要注意先将垃圾按照扔下来的时间\(T_i\)排序。
我们可以用\(f_{i,j}\)表示当前扔下了第\(i\)个垃圾,叠起来的总高度为\(j\)时还能存活的最大单位时间。
我们可以用\(f_{x,y}=-1\)来表示这种情况不存在。
那么依据题意,初始化时\(f_{0,0}=10,f_{0,1...M}=-1\).
由于一个垃圾有两种用途,因此我们可以分类讨论第\(i-1\)个垃圾的用途:
- 吃:\(f_{i,j}=f_{i-1,j}+F_{i-1}-(T_i-T_{i-1})\)
- 叠:\(f_{i,j}=f_{i-1,j-H_{i-1}}-(T_i-T_{i-1})\)
整理一下,就可以得出转移方程:$$f_{i,j}=max(f_{i-1,j}+F_{i-1},f_{i-1,j-H_{i-1}})-(T_i-T_{i-1})$$
不难发现,若\(f_{i,j}≠-1\)且\(j+H_i≥M\),就可以直接输出\(T_i\)并结束程序了。
这就是针对第一种情况的解法。
第二种情况:求最多能够存活的时间
第二种情况的解法是建立在第一种情况的\(DP\)的基础之上的。
可以看出,如果\(f_{i,j}\)不为0,那么奶牛还能活的时间最大为\(f_{i,j}+F_i\)(吃掉当前的垃圾),那么总共能活的时间就是\(f_{i,j}+T_i+F_i\)。
不难发现,只要\(f_{i,0}+T_i+F_i\)不为\(-1\),它肯定大于\(f_{i,x}+T_i+F_i(0<x≤M)\)和\(f_{y,0}+T_y+F_y(0≤y<i)\)的,因此,我们只要求出最大的满足\(f_{i,0}≠-1\)的\(i\),答案就是此时的\(f_{i,0}+T_i+F_i\)。
代码
#include<bits/stdc++.h>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define abs(x) ((x)<0?-(x):(x))
#define LL long long
#define ull unsigned long long
#define swap(x,y) (x^=y,y^=x,x^=y)
#define tc() (A==B&&(B=(A=ff)+fread(ff,1,100000,stdin),A==B)?EOF:*A++)
#define pc(ch) (pp_<100000?pp[pp_++]=(ch):(fwrite(pp,1,100000,stdout),pp[(pp_=0)++]=(ch)))
#define N 100
#define M 100
int pp_=0;char ff[100000],*A=ff,*B=ff,pp[100000];
using namespace std;
int n,m,f[N+5][M+5];
struct litter
{
int t,h,v;
}a[N+5];
inline void read(int &x)
{
x=0;int f=1;static char ch;
while(!isdigit(ch=tc())) f=ch^'-'?1:-1;
while(x=(x<<3)+(x<<1)+ch-48,isdigit(ch=tc()));
x*=f;
}
inline void write(int x)
{
if(x<0) pc('-'),x=-x;
if(x>9) write(x/10);
pc(x%10+'0');
}
inline bool cmp(litter x,litter y)//将垃圾按照扔下来的时间排序
{
return x.t<y.t;
}
int main()
{
register int i,j,ans=0;
for(read(m),read(n),i=1;i<=n;++i) read(a[i].t),read(a[i].v),read(a[i].h);
for(f[0][0]=10,i=1;i<=m;++i) f[0][i]=-1;//初始化
for(sort(a+1,a+n+1,cmp),i=1;i<=n;++i)//DP的核心代码
{
for(j=0;j<=m;++j)
{
if((f[i-1][j]<0||f[i-1][j]+a[i-1].v<a[i].t-a[i-1].t)&&((j>=a[i-1].h?f[i-1][j-a[i-1].h]:-1)<a[i].t-a[i-1].t)) {f[i][j]=-1;continue;}//如果当前状态无法转移到,就将f[i][j]赋值为-1,并跳过
if(j+a[i].h>=m) return write(a[i].t),fwrite(pp,1,pp_,stdout),0;//如果将当前垃圾叠起来的总高度大于m,就输出当前垃圾被扔下来的时间并退出程序
f[i][j]=max((j>=a[i-1].h?f[i-1][j-a[i-1].h]:-1),(f[i-1][j]>=0?f[i-1][j]+a[i-1].v:0))-(a[i].t-a[i-1].t);//求出f[i][j]
}
}
for(i=1;i<=n;++i) if(~f[i][0]) ans=f[i][0]+a[i].t+a[i].v;else break;//求出最大的满足f[i][0]≠-1的i,答案就是此时的f[i][0]+T[i]+F[i]
return write(ans),fwrite(pp,1,pp_,stdout),0;
}