P2347 砝码称重(动态规划递推,背包,洛谷)

题目链接:P2347 砝码称重

参考题解:点击进入

纪念我第一道没理解题意的题

''但不包括一个砝码也不用的情况'',这句话我看成了每个砝码起码放一个

然后就做不出来了

思路:

1.这题数据很小,1000,所以其实没必要把多重背包用二进制优化成01背包,直接一个个拆开就好了

2.建立数组f[2000],用来f[j]=1表示存在重量为j这种可能,f[j]=0则表示不存在这种可能

建立数组a[2000],用来把多重背包一个个拆开

比如说我们输入的是 1 0 0 1

那a就是{1,1,1,2,2,2,2,3,20}

数组f其实是用了"桶"的思想

数组start存一开始每个砝码的意义,这个就不用说了

3.对于输入数据和存入a数组那一步其实是一块完成的

int temp_n,t=0;
    for(int i=1;i<=6;i++)
    {
        cin>>temp_n;
        for(int j=1;j<=temp_n;j++)   a[++t]=start[i];
    }

4.边缘条件的定义  f[a[1]]=1;,这样i到时候直接从2开始

5.具体的递推过程

for(int i=2;i<=t;i++)
        for(int j=1000;j>=0;j--)
            if(f[j])
                f[j+a[i]]=1,f[a[i]]=1;

i=2~t表示遍历全部的砝码

j=1000~0表示遍历全部的重量,注意是j--,j是必须要下降的,如果从前面开始,那么

f[j+a[i]]=1的赋值可能会影响到后面的if(f[j])的判断

另外一个层面的理解就是从前往后可能就会出现把一个砝码放两次的情况,违背了我们刚刚拆分的01背包

6.最后把f数组扫一遍,每有一次true就ans++,然后ans就是答案

AC代码:

#include<bits/stdc++.h>
using namespace std;
bool f[2000];
int a[2000];
int main()
{
    std::ios::sync_with_stdio(false);
    int start[7]={0,1,2,3,5,10,20};

    int temp_n,t=0;
    for(int i=1;i<=6;i++)
    {
        cin>>temp_n;
        for(int j=1;j<=temp_n;j++)   a[++t]=start[i];
    }
    f[a[1]]=1;
    for(int i=2;i<=t;i++)
        for(int j=1000;j>=0;j--)
            if(f[j])
                f[j+a[i]]=1,f[a[i]]=1;
    int ans=0;
    for(int i=1;i<=1000;i++)
        if(f[i])
            ans++;
    printf("Total=%d",ans);
    return 0;
}

 

原文地址:https://www.cnblogs.com/zyacmer/p/10015838.html