P2347 NOIP1996 提高组 砝码称重

发布时间 2023-11-04 13:19:54作者: 加固文明幻景

P2347 NOIP1996 提高组 砝码称重

最初思路

看出来是多重背包,但是第一次用于求方案数,一开始想的是累加。但是实现起来发现结果很抽象,想想也不是那么回事。比如从样例上来说,F[3] = 1F[2] = 1F[1] = 1,显然F[3] != F[1] + F[2]

改进思路

然后受到启发,决定用打标记的思想,即若重量\(j\)是可行的,就用F[j]标记,然后用动态规划转移这些标记。

F[J] = F[j - w[i]] ? 1 : 0

思路其实是正确的,但是代码实现出了很多问题。

代码实现

\(8pts\)code
#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

int v[7], w[7] = {0, 1, 2, 3, 5, 10, 20};
int F[1010];
int sum = 0;
int ans;

int main()
{
	for (int i = 1; i <= 6; i++)
	{
		cin >> v[i];
		sum += v[i] * w[i];
	}
	for (int i = 1; i <= 6; i++)
	{
		F[w[i]] = 1;
	}
	for (int i = 1; i <= 6; i++)
	{
		for (int j = sum; j >= 0; j--)
		{
			for (int k = 1; k <= v[i]; k++)
			{
				if (k * w[i]  <= j)
				{
					if (F[j - k * w[i]])
						ans++;
				}
				else
				{
					if (F[j])
					{
						continue;
					}
				}

			}
		}
	}
	printf("Total=%d", ans);
	return 0;
}
首先初始化问题
  • 这是一个不能想当然的过程,我想当然把所有初始砝码重量都赋值为1。
  • 直到在每次ans++时调试输出\(j\)才发现问题,最终总结出应把F[0] = 1即可。
if顺序问题
  • 本题的\(F[j]\)是完全有可能在\(j >= k*w[i]\)时也被标记过的,因为这是标记该重量可行,答案只需统计一次,而if (F[j]) continue;则是为了避免重复统计答案,但是该语句放到else后则无法处理\(j >= k*w[i]\)\(F[j]\)被标记过的情况。
ACcode
#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

int v[7], w[7] = {0, 1, 2, 3, 5, 10, 20};
int F[1010];
int sum = 0;
int ans;

int main()
{
	for (int i = 1; i <= 6; i++)
	{
		cin >> v[i];
		sum += v[i] * w[i];
	}
	F[0] = 1;
	for (int i = 1; i <= 6; i++)
	{
		for (int j = sum; j >= 0; j--)
		{
			for (int k = 1; k <= v[i]; k++)
			{
				if (F[j])
				{
					continue;
				}
				if (k * w[i]  <= j)
				{
					if (F[j - k * w[i]])
						F[j] = 1, ans++;
				}
			}
		}
	}
	printf("Total=%d", ans);
	return 0;
}