P1156 垃圾陷阱

1|0P1156 垃圾陷阱

1|0基本思路

受这题的影响,我总觉得这题不应该直接把时间当作状态方程的值,于是搞了F[i][j],为前i个物品,前j时间内能到达的最大高度,然后又搞一个数组维护最优时间,但我的能力根本行不通。

#include<iostream> #include<algorithm> #include<cstdio> using namespace std; struct Trash { int t, h, f; bool operator < (const Trash& rhs) const { return t < rhs.t; } }; const int N = 101; int D, G; Trash a[N]; int F[N][1010], T[N]; int main() { cin >> D >> G;T[0] = 10; for (int i = 1; i <= G; i++) { cin >> a[i].t >> a[i].f >> a[i].h; } for (int i = 1; i <= G; i++) { T[i] = T[i - 1]; int timeI = (a[i].t - a[i - 1].t), timeSum = T[i]; if (T[i] - timeI <= 0) { T[i] = 10 + a[i].f; continue; } for (int j = 1; j <= a[G].t; j++) { F[i][j] = F[i - 1][j]; if (j >= timeI) { if (F[i][j] < F[i - 1][j - timeI] + a[i].h) { F[i][j] = F[i - 1][j - timeI] + a[i].h; T[i] = timeSum - timeI; } else { T[i] = timeSum + a[i].f; } } if (F[i][j] >= D) { cout << T[i]; return 0; } } } cout << T[G]; return 0; }

显然,这个算法只能保证在无生命限制下的最优解,无法兼顾生命限制来求最优解。事实上我的T[i]如果想正常维护的话可能还得开一个动态规划,但是我觉得太复杂了,没办法只能看题解。

1|0题解思路

1|0首先分析题目

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

1|0继续分析并使用排除法

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

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

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

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

那么j代表什么呢?

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

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

据说这个是叫离线

状态转移方程

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

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

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

状态转移方程

dp[i][j]=max(dp[i1][j]+trash[i].c,dp[i1][jtrash[i].h])

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


1|0实现该转移方程DP

1|0先讨论所谓的离线算法(我也不知道是不是这样叫的)

意思就是先处理完所有的状态奶牛可以达到的血量再与时间进行讨论

这里我们使用填表法(填表法就是利用状态转移方程和上一个状态来推导出现在的状态

dp[0][0] = 10,因为一开始有10

for(int i=1;i<=g;i++) for(int j=0;j<=d;j++) { if(dp[i-1][j]>=trash[i].t) dp[i][j]=max(dp[i][j],dp[i-1][j]+trash[i].c); if(j>=trash[i].h&&dp[i-1][j-trash[i].h]>=trash[i].t) dp[i][j]=max(dp[i][j],dp[i-1][j-trash[i].h]); }

需要非常非常注意的是状态的能否使用

对于以前的状态dp[i][j]能否使用,我们的要求是dp[i][j]>=trash[i].t它还没死

最后扫一遍

int maxh=0; int maxt=0; int i; for(i=1;i<=g;i++) { for(int j=0;j<=d;j++) { if(dp[i][j]-trash[i].t>=0) maxh=max(maxh,j); maxt=max(maxt,dp[i][j]); } if(maxh==d) break; } if(maxh==d) cout<<trash[i].t<<endl; else cout<<maxt<<endl;

这里的maxt实际只用讨论max(dp[i][0])就行了


1|0在线的和刷表法

刷表法就是利用当前的状态,把有关联的下一状态都推出来

在线的思路是,dp[i][j]所描述的血量是当前时间(既第i件物品落下时)所真正具有的血量,而不是忽略时间。

int maxt=0; for(int i=0;i<g;i++) { for(int j=0;j<=d;j++) { if(dp[i][j]>=trash[i+1].t-trash[i].t) { dp[i+1][j+trash[i+1].h]=max(dp[i+1][j+trash[i+1].h],dp[i][j]-(trash[i+1].t-trash[i].t)); dp[i+1][j]=max(dp[i+1][j],dp[i][j]+trash[i+1].c-(trash[i+1].t-trash[i].t)); } if(dp[i+1][j+trash[i+1].h]>=0&&j+trash[i+1].h>=d) { cout<<trash[i+1].t<<endl; return 0; } } if(dp[i][0]!=-1) maxt=max(maxt,dp[i][0]+trash[i].t); }

在这里我们边做边判断是否上去了并更新它的最长存活时间

1|0代码实现

#include<iostream> #include<algorithm> #include<cstdio> using namespace std; struct Trash { int t, h, f; bool operator < (const Trash& rhs) const { return t < rhs.t; } }; const int N = 101; int D, G; Trash a[N]; int maxH, maxT; int F[N][1010]; int main() { cin >> D >> G; for (int i = 1; i <= G; i++) { cin >> a[i].t >> a[i].f >> a[i].h; } sort(a + 1, a + G + 1); F[0][0] = 10; for (int i = 1; i <= G; i++) { for (int j = 0; j <= D; j++) { if (F[i - 1][j] >= a[i].t) { F[i][j] = max(F[i][j], F[i - 1][j] + a[i].f); } if (j >= a[i].h && F[i - 1][j - a[i].h] >= a[i].t) { F[i][j] = max(F[i][j], F[i - 1][j - a[i].h]); } } } for (int i = 1; i <= G; i++) { for (int j = 0; j <= D; j++) { if (F[i][j] >= a[i].t) { maxH = max(maxH, j); } maxT = max(maxT, F[i][j]); } if (maxH == D) { cout << a[i].t; return 0; } } cout << maxT; return 0; }

__EOF__

本文作者Kdlyh
本文链接https://www.cnblogs.com/kdlyh/p/17810629.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   加固文明幻景  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示