交大OJ 1013 无限背包(0-1背包) 错解分析&&正解

  1. 无限背包
    Description
    你现在有一个体积为V的大袋子,有N种物品,假设每种物品的数量有无限多个,而且第i种物品的体积是c[i],价值是w[i],请选择一些物品放入袋中,使袋中物品的价值总和最大。
    注意每种物品的数量是无限多的;对于放入袋中的同种物品数量没有限制。
    Input Format
    第一行包含两个正整数V和N,分别代表袋子的体积和物品的种类数。
    以下N行分别由2个正整数组成,代表每种物品的体积和价值。
    V≤10000,N≤1000
    V≤10000,N≤1000

    保证操作可在C++ int范围内完成。
    Output Format
    输出一个整数,表示最大的价值总和
    Sample Input
    5 3
    2 3
    3 2
    4 1
    Sample Output
    6

错解一:


#include <iostream>
#include <cstdio>
using namespace std;
const long long M=10001;
bool dpall[M];
long long c[1001];
long long w[1001];
long long sets=0;
long long alldata[M];
long long cdata[M];
long long V;
long long N;
long long Max=0;
void setdpallAhead()
{
    for(int i=0;i<M;i++)
    {
        dpall[i]=false;
        alldata[i]=0;
        cdata[i]=0;
    }

}

void setdp()
{   setdpallAhead();
    sets=0;
    for(int i=1;i<=N;i++)
    {
        if(i==1)
        {
            for(int k=1;k*c[1]<=V;k++)
            {
                dpall[k*w[1]]=true;
                alldata[++sets]=k*w[1];
                cdata[sets]=k*c[1];
                Max=max(k*w[1],Max);
            }
        }
        else
        {   long long presets=sets;
            for(int j=0;j<=presets;j++)
            for(int k=1;cdata[j]+k*c[i]<=V;k++)
            {
                if(!dpall[alldata[j]+k*w[i]])
                {dpall[alldata[j]+k*w[i]]=true;
                 alldata[++sets]=alldata[j]+k*w[i];
                 cdata[sets]=cdata[j]+k*c[i];
                 Max=max(alldata[j]+k*w[i],Max);
                }
            }
        }
    }
}

int main()
{
    scanf("%d%lld",&V,&N);
    for(int i=1;i<=N;i++)
    {
        scanf("%lld",&c[i]);
        scanf("%lld",&w[i]);
    }
    setdp();
    printf("%lld\n",Max);
    return 0;
}

错因分析:M的值不应该是10001,是体积小于10000但是dpall[i]的地址储存的是权值,权值只能确定是在int能表示的范围之内,把M改成1000000,程序就正确了但是在OJ上面是超时的。

错解二:

#include <iostream>
#include <cstdio>
using namespace std;
const long long M=10001;
long long dpall[M];
long long c[1001];
long long w[1001];
long long sets=0;
long long alldata[M];
long long cdata[M];
long long V;
long long N;
long long Max=0;
void setdpallAhead()
{
    for(int i=0;i<M;i++)
    {
        dpall[i]=-1;
        alldata[i]=-1;
        cdata[i]=-1;
    }

}

void setdp()
{   setdpallAhead();
    sets=0;
    Max=0;
    for(int i=1;i<=N;i++)
    {
        if(i==1)
        {
            for(int k=1;k*c[1]<=V;k++)
            {
                dpall[k*c[1]]=k*w[1];
                alldata[++sets]=k*w[1];
                cdata[sets]=k*c[1];
                Max=max(k*w[1],Max);
            }
        }
        else
        {   long long presets=sets;
            for(int j=0;j<=presets;j++)
                for(int k=1;cdata[j]+k*c[i]<=V;k++)
            {
                    if(dpall[cdata[j]+k*c[i]]==-1)
                    {
                        dpall[cdata[j]+k*c[i]]=alldata[j]+k*w[i];
                        alldata[++sets]=alldata[j]+k*w[i];
                        cdata[sets]=cdata[j]+k*c[i];
                        Max=max(alldata[j]+k*w[i],Max);
                    }
            }
        }
    }
}

int main()
{
    scanf("%d%lld",&V,&N);
    for(int i=1;i<=N;i++)
    {
        scanf("%lld",&c[i]);
        scanf("%lld",&w[i]);
    }
    setdp();
    printf("%lld\n",Max);
    return 0;
}

错因分析:与错解一相比,这里缩小了dpall数组的长度,但是答案错误,愿意很简单,这里的dpall[ i]地址i代表是体积大小为i但是存在这种情况:两个不同的组合体积相同但是权值不同,这怎么办?因此答案错误。不能用同一个数组同时储存体积和权值
总结: dpall【i】代表背包其元素地址i存储的是你要求的值,元素值要么是0要么是1,不可“一心二用”,比如这里求的事最大权值,你就应该让i代表权值而不是体积,但是这道题如果采取这种算法,一定会超时,时间耗费太大,因此应该换一种算法

正解代码如下:

#include <iostream>
#include <cstdio>

using namespace std;

int max(int a, int b) {
    if (a > b) return a;
    else return b;
}

int main() {
    int V, N, c[1001], w[1001], f[10001];
    scanf("%d", &V);
    scanf("%d", &N);
    int i, v;
    for (i = 1; i <= N; i++) {
        scanf("%d", &c[i]);
        scanf("%d", &w[i]);
    }
    for (v = 0; v <= V; v++) {
        f[v] = 0;
    }
    for (i = 1; i <= N; i++) {
        **for (v = c[i]; v <= V; v++)** {//起点是c[i],而且是从c[i]开始正序循环,递增循环,正序循环表示c[i]的数量有无限个!!

            f[v] = max(f[v], f[v - c[i]] + w[i]);//这里通过递归f[v-c[i]]一定是最优解,如果加上w[i]权值比f[v]大,那么就是最优解。
        }
    }

    printf("%d", f[V]);//完成递归以后f[V]就是最大权值


    return 0;
}

算法:f[v]=max(f[v], f[v - c[i]] + w[i]);
f[v]相当于背包,地址v代表占用体积v,f[v]的值代表占用体积<=v物品的价值,通过以上算法递归完成后,f[ ]数组当中的每个数都是最大价值,f[V]就是最大价值;
特别注意代码三中加粗部分,是从c[i]开始正序循环,递增循环,正序循环表示c[i]的数量有无限个!!如果是递减循环即从从V开始递减循环到c[i]就表明这一次循环当中只能用一个c[i],对比博主下一条博客————有限背包,见参考资料。

参考资料:
0-1背包问题详解(java):http://shmilyaw-hotmail-com.iteye.com/blog/2009761
有限背包博客:http://blog.csdn.net/biwendong/article/details/78670173

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值