0-1背包

问题:

给定n种物品和一背包。物品i的重量是wi,其价值为vi,背包的容量为C。问:应该如何选择装入背包的物品,使得装入背包中的物品总价值最大

分析:

Xi表示第i物品,用1表示选了该物品,用0表示未选该产品,那么Xi*vi就是所选物品的价值,求和后就是选了的总价值。第二个条件就是:我们所选的总物件重量不能超过背包容量

综上我们得到公式:

 

方案

方案一:从头部减少

将第一个物品去掉,剩下X={x2,x3,....xn};

那么背包容量就需要去掉x1,就用算从x2到xn的重量和,并且最大的价值也要从x2到xn算起(从头部缩短问题规模)

Sum(Wi*xi)<=c-w1 *x1;Max(sum(vi,xi)) ; i=2,3,.....n

 

等等等等一直到n结束,此时当等于n时就是问题规模就为1

设计:

1.用m表示最优解,有两个因素 物品的数量(i) 和当前背包的能够存放的重量(j)

注意:i是一个区间:i,i+1,i+2,....,n;

2.设计基本元素后,我们从最简单情况开始,当i==n时表示只有一个元素,我们只需要判断他的重量与当前背包容量,如果小于当前容量则可以存放,如果大于则不能

即 j>= w[n]? v[n]:0;

3.接下来就到了下一个,如果当前背包容量小于你所选的那个物品重量,那么我们就第i+1开始选,跳过当前物品。否则:分两种情况:如果选择当前物品,则对应的当前背包容量减去当前物品重量,价值加上当前物品价值,如果不选择,则从第i+1开始选,需要对这两种情况的价值进行比较,去最大的结果

得到状态转移方程

 

编写程序:

第一:递归方法

完全按照动归方程

int Knapsance(int V[], int W[], int i, int n, int j) {
    if (i == n) {
        if (j >= W[i]) {
            return V[i];
        }
        else {
            return 0;
        }
    }
    else {
        if (j < W[i]) {
            return Knapsance(V, W, i + 1, n, j);
        }
        else {
            int v1 = Knapsance(V, W, i + 1, n, j);
            int v2 = Knapsance(V, W, i + 1, n, j - W[i]) + V[i];
            if (v1 > v2) {
                return v1;
            }
            else {
                return v2;
            }
        }
    }
}

第二:非递归(进一步优化问题)

需要有一个空间去存储值

 

先判断第五个是否能放,然后依次判断4 3 2 1

int NiceKnapsance(int V[], int W[], int  n, int c,vector<vector<int>>&m) {
    for (int j = 1; j <= c; ++j) {
        m[n][j] = j >= W[n] ? V[n] : 0;
    }
    Printvector(m);
    for (int i = n - 1; i >= 1; --i) {
        for (int j = 1; j <= c; ++j) {
            if (j < W[i]) {
                m[i][j] = m[i + 1][j];
            }
            else {
                m[i][j] = std::max(m[i + 1][j], m[i + 1][j - W[i]] + V[i]);
            }
        }
        Printvector(m);
    }
    return  m[1][c];
}

结果如图所示

方案二:从尾部减少

在头删那块我们用i来表示从x1一直到xn;

在尾部这:判断最后一个是否选取后,那么我们应该是从1到n-1即i-1

动态方程与头部删除唯一不同的就在于i-1还是i+1

递归:

int Knapsance2(int V[], int W[], int i, int j) {
	if (i == 1) {
		if (j >= W[i]) {
			return V[i];
		}
		else {
			return 0;
		}
	}
	else {
		if (j < W[i]) {
			return Knapsance2(V, W, i - 1, j);
		}
		else {
			int v1 = Knapsance2(V, W, i - 1, j);
			int v2 = Knapsance2(V, W, i - 1,  j - W[i]) + V[i];
			if (v1 > v2) {
				return v1;
			}
			else {
				return v2;
			}
		}
	}
}

非递归

int NiceKnapsance_2(int V[], int W[], int  n, int c, vector<vector<int>>& m) {

	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= c; j++) {

			if (j < W[i]) {
				m[i][j] = m[i - 1][j];
			}
			else {
				int x1 = m[i - 1][j];
				int x2 = m[i - 1][j - W[i]] + V[i];
				if (x1 > x2) {
					m[i][j] = x1;
				}
				else {
					m[i][j] = x2;
				}
			}
		}
			Printvector(m);
		}
		return m[n][c];
	
}

综上我们不难发现,无论是从头开始切还是从尾部开始切所得到的最大价值是一样的,但是他们的划分方法不同,导致矩阵每一个元素不同

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值