题目连接 洛谷P2347
题目描述
设有1g、2g、3g、5g、10g、20g的砝码各若干枚(其总重≤1000 ),
输入格式
输入方式:a1,a2,a3,a4,a5,a6
(表示1g砝码有a1个,2砝码有a2个,…,20g砝码有a6个)
输出格式
输出方式:Total=N
(N表示用这些砝码能称出的不同重量的个数,但不包括一个砝码也不用的情况)
首先拿到题目,尝试枚举法,结果超时。
之后分析问题:
题目给了一些物品,给了总空间1000,并且有权值和重量两个属性。在01背包问题中,因为每种物品只有一个,对于每个物品只需要考虑选与不选两种情况。如果不选择将其放入背包中,则不需要处理。
动态转移方程 f[i][j] = f[i-1][j] || f[i-1][j-weight[i]]
这里的i指的是第i个砝码,比如数据1 2 3 4 5 6,i = 4的时候就是指第一个重量为3的砝码 f是用于标记我们是不是访问过这个状态
j指的就是重量,指前i个砝码放上去的总重量是多少。
我认为01背包本质上是利用数组把之前算过的部分算了,进而减少运算次数。把上一次的状态记好,存下来,再有重复的时候可以直接调出来。
因为本题没有说要求最大的情况,而是说要求所有次数。所以状态转移方程并不是用max{f[n], f[n-1]},而是根据之前枚举的所有状态来推得能否得到现在的状态。
在代码书写时我出现了一个问题,开始我是遍历了所有flag数组去求解类别,后来发现只要遍历i=n的时候就可以了,因为因为之前的(比如当i=1时,j=1这种情况就是我拿了一个1g的砝码,然而flag[2][1]也是一样的,相当于没放2g的砝码,但是最后的重量都是1)。
原来的解法打了注释(结果还是不对,开了个数组保证weight的唯一性,但好像不行)
// 01背包实现砝码称重问题
// 本质上是利用数组把之前算过的部分算了,进而减少运算次数。把上一次的状态记好,存下来,再有重复的时候可以直接调出来。
// 因为本题没有说要求最大的情况,而是说要求所有次数。所以状态转移方程并不是用max{f[n], f[n-1]},而是根据之前枚举的所有状态来推得能否得到现在的状态。
// 状态转移方程应该是 f[i][j] = f[i-1][j] || f[i-1][j-weight[i]],若这两个状态可以达到,那么f[i][j]的状态也就可以达到。
#include <stdio.h>
int flag[1100][1100] = {
0};
int weight[1100] = {
0};
int val[1100] = {
0};
int main(