0-1背包问题

#include<iostream>
using namespace std;
#define NUM 50//物品数量上限
#define CAP 1500//背包容量上限
int w[NUM];//物品的重量
int v[NUM];//物品的价值 
int x[NUM];//记录哪个物品被选择
int p[NUM][CAP];//用于递归的数组 p[i][j]的意思是,当背包容量暂时限定为j时,物品从n,n-1....i挑选。
void knapsack(int n,int c)//n是物品的数量,c是背包的容量
{
    //第0行不用,第0列使用,最后答案在p[1][c]。右上角
     int jMax = min(w[n] - 1, c);//c一般比较大,这里可以理解为jmax=w[n]-1;
    for (int j = 0; j<= jMax; j++)//j<w[n];第n个物品的重量为w[n],当背包容量比w[n]小时,放不开。
    {
        p[n][j] = 0;//在这个范围的j,p[n][j]=0;
    }
    for (int j = w[n]; j <= c; j++)//j>=w[n],j从恰巧能放开开始,到最大容量c.
    {
        p[n][j] = v[n];//此时只放第n个物品,所以p[n][j]=v[n];
    }
    for (int i = n - 1; i > 0; i--)//从倒数第二行到第一行。
    {
        jMax = min(w[i] - 1, c);
        for (int j = 0; j <= jMax; j++)
        {
            p[i][j] = p[i+1][j];//j<w[i],在这个范围第i个物品放不开,相当于从第i+1开始。
        }
        for (int j = w[i]; j <= c; j++)//在能装下的情况下,可装可不装,不装同上,
            //装了就相当于从i+1开始往后找,背包容量修改为装了剩余之后的容量,最后加上装入的价值。
        {
            p[i][j] = max((p[i + 1][j]),(p[i + 1][j - w[i]] + v[i]));//理应从以上两种情况取较大的。
        }

    }
}
void traceback(int n,int c)//用x数组记录哪件物品被选中,选中的值为1.
{
    for (int i = 1; i < n; i++)//第一行到倒数第二行。
    {
        if (p[i][c] == p[i + 1][c])//如果p[i][c]==p[i+1][c] ,说明有没有第i件物品都一样
        {
            x[i] = 0;//如果p[i][j]==p[i+1][j],说明不用选。
        }
        else
        {
            x[i] = 1;
            c = c - w[i];//被选上,背包容量变小。
        }
    }
    if (p[n][c] == 0)//如果最后一行的数等于0 就是没选。反之就是选了。
    {
        x[n] = 0;
    }
    else
    {
        x[n] = 1;
    }
    cout << "选中的背包号是" << endl;
    for (int i = 1; i <= n; i++)
    {
        if (x[i] == 1)
        {
            cout << i << " ";
        }
    }
}

int main()
{
    int n,c;
    cout << "请输入物品的数量和背包的容量" << endl;
    cin >> n>>c;
    int x, y;
    for (int i = 1; i <= n; i++)
    {
        cout << "请输入第"<<i << "个物品的重量和价值" << endl;
        cin >> x >> y;
        w[i] = x;
        v[i] = y;
    }
    knapsack(n,c);
    cout << "背包的最大容量为" << p[1][c] << endl;
    traceback(n,c);


}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值