背包问题

动态规划初步
动态规划算法通常基于一个递推公式及一个或多个初始状态。 当前子问题的解将由上一次子问题的解推出。使用动态规划来解题只需要多项式时间复杂度, 因此它比回溯法、暴力法等要快许多。以背包问题为例
01背包问题
有n个重量和价值分别为wi和vi的物品,从这些物品中挑选出总价值不超过W的物品,求这些方案中价值最大的一种。这类问题最易想到的就是朴素递归进行穷竭搜索,但是递归过程无论是算法复杂度还是空间占有率都比较高。我们可以将这个问题转化为递推的问题,建立二维数组大盘dp[i][j],dp[i][j]表示从0到i这i+1个物品的总重量不超过j的物品的总价值最大者,则递推代码可以这样描述

void solve() 
{
    for (int i = 0; i < n; i++)
  { 
      for (int j = 0; j <= W; j++)
     { 
       if (j < w[i])//无法挑选物品 
        dp[i + 1][j] = dp[i][j]; 
  else//挑和不挑中选一个价值更大的
   dp[i + 1][j] = max(dp[i][j], dp[i][j - w[i]] + v[i]); 
      }
  }
    cout << dp[n][W] << endl; }

上面利用动态数组的算法都是利用的二维数组dp,其实我们会发现,利用递推关系时,每次需要改变值的数组都是在同一行的,所以我们可以不断的覆盖前一行,只利用一维数组便可以求解。

void solve()
{
    for (int i = 0; i < n; i++)
    {
        for (int j = W; j >= w[i]; j--)  //从后往前循环
            dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
    }
}
      cout << dp[W];
}

由01背包问题可以衍生出完全背包问题,完全背包问题中,物品无限多件,只要背包没满,就可以一直添加,这类问题可以想之前一样考虑二维数组储存,但同时也可以借鉴之前的思路将代码优化到一维,与01背包不同的是此时需要从前往后循环遍历

#include<bits/stdc++.h>
using namespace std;
int w[10],v[10],dp[105];
int main()
{
	int m,n;//m为 容量  n为物品种类
	cin >> m >> n;
	for(int i = 1;i <= n;i ++)
	cin >> w[i] >> v[i];
	
	for(int i = 1;i <= n;i ++){
		for(int j = 1;j <= m;j ++)
		if(j >= w[i])
		dp[j] = max(dp[j],dp[j-w[i]] + v[i]);
	} 
	
	cout << dp[m];
	return 0;
}

还有一种基本背包问题多重背包,是每件物品都有给定的数量,如果按照安全背包的思想,每件物品的数量为num[i],即1<=k<=num[i],可以将问题转化为01背包问题

public static int bag4(int W, int[] w, int[] v, int[] num) {
		int n = w.length - 1;// 第一个值,不算
		int[] f = new int[W + 1];
		for (int i = 1; i <= n; i++)
			for (int j = W; j >= w[i]; j--)
				for (int k = 0; k <= num[i]; k++) {
					if (j - k * w[i] >= 0 && f[j - k * w[i]] + k * v[i] > f[j])
						f[j] = f[j - k * w[i]] + k * v[i];
				}
 
		return f[W]; // 最优解
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值