完全背包问题

01背包问题

前面分析过01背包问题。01背包:在M件物品中取出若干件物品放到背包中,每件物品对应的体积v1,v2,v3,....对应的价值为w1,w2,w3,,,,,每件物品最多拿一件。

解决方法是 动态规划

决策是:第i件物品放或者不放

由此得到状态转移方程:

    f[i,j] = max{f[i-1,j], f[i-1,j-wi]+Pi(j>=wi)}

f[i,j]表示前i件物品中选择若干件放在所剩空间为j的背包中。wi表示第i件物品的体积。Pi表示第i件物品的价值。


上述方程可优化为一维数组表达式:

n是物品种类数,V是背包容量,c[i]是物品i的体积,w[i]是物品i的价值,f[v]是容量为v的背包在前i件物品中取得的最大价值
for(int i=1;i<=n;++i){
        for(int v =V;v>=c[i];v--){
            f[v] = max(f[v],f[v-c[i]]+w[i]);
            cout <<"i: "<
这里注意次序,第二个for循环for(int v =V;v>=c[i];v--)依次减小的。

完全背包问题

最优解: O(VN)

       for i=1..N

  for j=0..V

  f[j]=max{f[j],f[j-c]+w}

上面的伪代码和01背包不同之处只有v的循环次序不同。

       为什么01背包要按照v的逆序来循环。正是为了保证每件物品i只选择一次。为了保证在考虑“选入第i件物品”这件策略时,依据的是一个没有已经选入第i件物品的子结果f[v-c]。

    然而,完全背包的特点恰是每种物品可选无限件,所以,必须采用v的顺序循环。

 完全背包:

有N种物品和一个容量为V的背包,每种物品都有无限件可用。

  第i种物品的体积是c,价值是w。求解将哪些物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大。这里不同之处是每件物品可无限取,这里就产生了很多可行的优化,比如同体积的有多种物品,则必然可以舍弃价值小的,也可以舍弃体积大于v的。


直接说题意,完全背包定义有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的体积是c,价值是w。求解将哪些物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大。本题要求是背包恰好装满背包时,求出最大价值总和是多少。如果不能恰好装满背包,输出NO

输入
第一行: N 表示有多少组测试数据(N<7)。 
接下来每组测试数据的第一行有两个整数M,V。 M表示物品种类的数目,V表示背包的总容量。(0接下来的M行每行有两个整数c,w分别表示每种物品的重量和价值(0
输出
对应每组测试数据输出结果(如果能恰好装满背包,输出装满背包时背包内物品的最大价值总和。 如果不能恰好装满背包,输出NO)
样例输入
2
1 5
2 2
2 5
2 2
5 1
样例输出                                      
              NO

             1

强调一点:这里唯一不同的是背包如果不能完全装满,则输出NO,这里需要一个技巧就是初始化时f[0]=0,其余的均为-max(初始化一个大的负数),只有这样最大值为正时,只能通过f[0]在相加其他价值得到,如:

背包体积为4时, 一种物品体积2,价值2;  则   f[0]=0; f[1]=-max; f[2]=max(f[2],f[0]+w[i])=2; 注意若背包不需要全部装满时,f[3]本该为2的,但此时f[3]=max(f[3],f[1]+2)=max(f[3],2-max)=2-max; 负无穷。最后结果就是如果f[v]是负值表示,背包不能完全装满,需要输出NO。如果为正值,表示背包可以装满输出结果即可。
如果很难理解,可以看程序,程序一步一步看,更容易理解。
#include  
#include  
#include  
#include  
        using namespace std;  
    int f[50010], c[2010], w[2010];  
    int main()  
    {  
        int test, m, v, i, j;  
        scanf("%d", &test);  
        while (test--)  
        {  
            memset(f, -10000000, sizeof(f)); //用来判断背包是否装满  
            f[0] = 0;  
            scanf("%d%d", &m, &v);  
            for (i = 1; i <= m; ++i)  
                scanf("%d%d", &c[i], &w[i]);  
            for (i = 1; i <= m; ++i)  
            for (j = 0; j <= v; ++j)//注意此循环与01背包的用一维数组表示的状态方程的区别,一个循环逆序,一个顺序  
            if (j >= c[i])  
                f[j] = f[j]>(f[j - c[i]] + w[i]) ? f[j] : f[j - c[i]] + w[i];//完全背包的状态方程,可画图加深理解  
            if (f[v]<0)//背包为装满  
                printf("NO\n");  
            else  
                printf("%d\n", f[v]);  
        }  
    }  
另外,还有一种完全背包题型:是背包不需要装满,输出最大值,此时只需要把f[N]全部初始化为0即可,不需要初始化除f[0]外,其他值为最大负数。


你可能感兴趣的:(动态规划,蓝桥杯)