Cut Ribbon

1|0Cut Ribbon

1|1基本思路

一眼完全背包,然而样例全过却无法AC。

看了提示之后明白这是一个要求必须完全装满的完全背包。

意思就是纸带剪完的剩余也得是要求的长度。

我一开始的想法是打标记,所有非要求长度的都标记成负数,然后要求长度的F数组设为1。

for (int i = 0; i <= 5010; i++) { F[i] = -100000; } for (int i = 1; i <= 3; i++) { F[v[i]] = 1; } for (int i = 1; i <= 3; i++) { for (int j = v[i]; j <= n; j++) { F[j] = max(F[j], F[j - v[i]] + 1); } }

然而这是可以证伪的。

因为这只标记了要求长度的一倍,当要求长度的n倍,或者两个要求长度的线性组合出现时都是被标记成负一的,除非同步更新标记

然而我并没有想到怎么同步更新标记。

1|2题解思路

实际上也是标记。

但是巧妙地通过状态转移方程来更新了。

首先把所有点打上1标记。

然后只给F[0]赋值为0

之后标记的更新就交给了转移方程。

for (int i = 1; i <= 3; i++) { for (int j = v[i]; j <= n; j++) { if (F[j - v[i]] != -1) F[j] = max(F[j], F[j - v[i]] + 1); } }

这个过程很巧妙的避免了背包不满的情况。

写到一半冲去西二英语课点名了

具体地,比如第一轮更新时肯定只能更新到F[0]1的状况,也就是更新了第一个物品的状态和标记。

第二轮则只能从刚好装满第一个物品的状态下转移。

以此类推。

1|3代码实现

#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; int n; int v[4]; int F[5010]; int main() { cin >> n; cin >> v[1] >> v[2] >> v[3]; for (int i = 0; i <= 5010; i++) { F[i] = -1; } F[0] = 0; for (int i = 1; i <= 3; i++) { for (int j = v[i]; j <= n; j++) { if (F[j - v[i]] != -1) F[j] = max(F[j], F[j - v[i]] + 1); } } cout << F[n]; return 0; }

__EOF__

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