P5662 CSP-J 2019 纪念品

1|0P5662 CSP-J 2019 纪念品

1|1基本思路

1|0状态方程

满头大汗地想了一个半小时,推导出一个可行的状态方程。

F[i][j][k]表示第i天,前j种纪念品,花费k金币所能得到的第二天最大卖出价格

1|0状态转移

首先第一维明显可以用滚动数组优化。

然后就是枚举k时的最大值问题,这里我用一个m维护一轮中最大的F[i][j][k],也是可行的。

我把购买当前物品直接等价成价值先减去本轮物品的价格再加上下一轮该物品的价格,也是可行的。

然而最关键的步骤,状态转移方程,我推不对。

理论上应该有三个状态

  • 不操作当前物品(不买不卖)
    • F[i][j][k]=F[i1][j1][k]
    • 这个好说
  • 购买当前物品
    • F[i][j][k]=max(F[i][j][k],F[i1][j1][?]+w[i+1][j]w[i][j])
    • 这里F的第三维我犯难了,凭直觉我写了[kw[i][j]]
  • 卖掉当前物品
    • F[i][j][k]=max(F[i][j][k],F[i1][j1][?]+w[i][j])
    • 这里我几乎是没法推出来,无脑写了[kw[i1][j]]

因为第二个第三个状态的模糊,我的代码样例都跑不了。但还是先挂一下。

1|0代码实现

#include<iostream> #include<algorithm> #include<cstdio> using namespace std; int T, N, M, m; int w[110][110]; int F[110][10010]; int main() { cin >> T >> N >> M; m = M; for (int i = 1; i <= T; i++) { for (int j = 1; j <= N; j++) { cin >> w[i][j]; } } for (int i = 1; i <= T; i++) { M = m; m = 0; for (int j = 1; j <= N; j++) { for (int k = 0; k <= M; k++) { F[j][k] = F[j - 1][k]; if (k >= w[i][j]) { F[j][k] = max(F[j][k], F[j - 1][k - w[i][j]] + w[i + 1][j] - w[i][j]); //printf("buy the goods F[%d][%d] = %d\n", j, k, F[j][k]); } if (k >= w[i - 1][j]) { F[j][k] = max(F[j][k], F[j - 1][k - w[i - 1][j]] + w[i][j]); //printf("sell the goods F[%d][%d] = %d\n", j, k, F[j][k]); } m = max(m, F[j][k]); } } } cout << m; return 0; }

1|2改进思路

1|0状态方程

题解竟然和我一模一样。

1|0状态转移

这可就太值得说道说道了。

其实我对于不操作当前物品和购买当前物品的状态转移都是完全正确的。

然而第三个状况,也就是卖掉当前物品,不是不对,而是根本不应该考虑

因为结合我对状态方程的定义

i天,前j种纪念品,花费k金币所能得到的第二天最大卖出价格

以及我对物品的处理

所有物品的买入等价成第二天最大的卖出价格的增加。

实际上,所有当前我所拥有的物品都已经被当成价格而非物品来考虑了,并且也参与了该轮最大价格的构成。

另一方面,我当时在推导卖掉当前物品时万分别扭就是因为我状态方程的值是下一轮最大的卖出价格,而我卖掉当前物品,获利的是本轮,我无法用一个用来记录下一轮价格的状态来操作本轮的价格变化

换句话说,所谓卖掉当前物品这个情况,实际上已经被上一轮自动考虑了,即所有购买的物品都被自动卖出了,除非你不选择购买本轮的物品(即花掉上轮自动卖出的钱),否则影响是一直存在的,所以被购买的物品已经在上一轮被操作过了,在本轮不用也不能对它进行二次操作。

1|0代码实现

首先把卖掉当前物品的情况删去。

然后是m的更新方式要改变,因为状态方程计算的是纯利润,并没有加上本金。所以在每轮天数都用m加上状态方程更新的最大收益即可。

先贴上这样处理完的代码

#include<iostream> #include<algorithm> #include<cstdio> using namespace std; int T, N, M; int w[110][110]; int F[110][10010]; int main() { cin >> T >> N >> M; for (int i = 1; i <= T; i++) { for (int j = 1; j <= N; j++) { cin >> w[i][j]; } } for (int i = 1; i <= T; i++) { for (int j = 1; j <= N; j++) { for (int k = 0; k <= M; k++) { F[j][k] = F[j - 1][k]; if (k >= w[i][j]) { F[j][k] = max(F[j][k], F[j - 1][k - w[i][j]] + w[i + 1][j] - w[i][j]); } } M += F[N][M]; } } cout << M; return 0; }

然而还是不对,犯了一个严重的错误,又把完全背包和零一背包的处理混淆了

零一背包从F[j1][]来推自然无可厚非,问题是完全背包写成这个形式是通过比较两个状态推导出来的,本身就不是直接逻辑,由推导得出应该是从F[j][]推出才对。

终于AC

#include<iostream> #include<algorithm> #include<cstdio> using namespace std; int T, N, M; int w[110][110]; int F[110][10010]; int main() { cin >> T >> N >> M; for (int i = 1; i <= T; i++) { for (int j = 1; j <= N; j++) { cin >> w[i][j]; } } for (int i = 1; i <= T; i++) { for (int j = 1; j <= N; j++) { for (int k = 0; k <= M; k++) { F[j][k] = F[j - 1][k]; if (k >= w[i][j]) { F[j][k] = max(F[j][k], F[j][k - w[i][j]] + w[i + 1][j] - w[i][j]); } } M += F[N][M]; } } cout << M; return 0; }

__EOF__

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