P2347 NOIP1996 提高组 砝码称重
P2347 NOIP1996 提高组 砝码称重
最初思路
看出来是多重背包,但是第一次用于求方案数,一开始想的是累加。但是实现起来发现结果很抽象,想想也不是那么回事。比如从样例上来说,F[3] = 1
,F[2] = 1
,F[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;
}