Cut Ribbon

发布时间 2023-11-06 15:58:24作者: 加固文明幻景

Cut Ribbon

基本思路

一眼完全背包,然而样例全过却无法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\)标记。

然后只给\(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] \neq - 1\)的状况,也就是更新了第一个物品的状态和标记。

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

以此类推。

代码实现

#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;
}