洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法
填表法 :就是一般的动态规划,当前点的状态,可以直接用状态方程,根据之前点的状态推导出来。
刷表法:由当前点的状态,更新其他点的状态。需要注意:只用当每个状态所依赖的状态对它的影响相互独立。
下面会用一道题目给出两种写法的区别
题目链接:P1156 垃圾陷阱
题意:
吃垃圾就完了卡门――农夫约翰极其珍视的一条
Holsteins
奶牛――已经落了到“垃圾井”中。“垃圾井”是农夫们扔垃圾的地方,它的深度为$D(2 \le D \le 100)$英尺。卡门想把垃圾堆起来,等到堆得与井同样高时,她就能逃出井外了。另外,卡门可以通过吃一些垃圾来维持自己的生命。
每个垃圾都可以用来吃或堆放,并且堆放垃圾不用花费卡门的时间。
假设卡门预先知道了每个垃圾扔下的时间$t(0< t \le 1000)$,以及每个垃圾堆放的高度$h(1 \le h \le 25$)和吃进该垃圾能维持生命的时间$f(1 \le f \le 30)$,要求出卡门最早能逃出井外的时间,假设卡门当前体内有足够持续$10$小时的能量,如果卡门$10$小时内没有进食,卡门就将饿死。
注:如果能量为 $0$ 时恰好有垃圾可以吃,则不会饿死
注意到对于每个垃圾,只有吃掉和堆起来两种方法
设 $dp[i][j]$ 表示只考虑前 $i$ 个垃圾,堆到高度 $j$ 时,最多可以活到什么时候
容易推出方程
1.填表法
求出dp数组以后扫一遍就好了,细节较多(其实好像可以滚动数组,但是懒 qwq)
注意这里垃圾的高度可能会超过 $m$ ,但是数据好像没卡这个东西
处理方法就是把 $m+\max(h_i)$ 都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方程
转载请说明出处