动态规划 背包问题

01背包

有n件物品和容量为m的背包 给出i件物品的重量以及价值 求解让装入背包的物品重量不超过背包容量 且价值最大
这是最简单的背包问题 特点是每个物品只有一件供你选择放还是不放
设dp[i]表示重量不超过i公斤的最大价值 可得出状态转移方程 dp[i]=max(dp[i],dp[i-weight[i]]+value[i])
前者表示不取还是上一次状态的dp[i],后者表示取,减去重量加上价值

		for(int i=1;i<=n;i++)
		{
			for(int j=m;j>=weight[i];j--)//此处逆序
			{
				dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);
			}
		}

那一步逆序的原因是为了避免同一个物品取了两次,如假设有重量为{2,3,4} 价值为{3,4,5}三个物品,如果那一层循环为顺序
则在第一次循环中算出 dp[2]=3,dp[4]=max(dp[4],dp[2]+value[1])=6,就产生了拿了两次一号物品导致出错!!

完全背包

有n件物品和容量为m的背包 给出i件物品的重量以及价值 求解让装入背包的物品重量不超过背包容量 且价值最大
特点 题干看似与01一样 但它的特点是每个物品可以无限选用
设dp[j]表示重量不超过i公斤的最大价值 可得出状态转移方程 dp[j]=max(dp[j],dp[j-weight[i]]+value[i])

		for(int i=1;i<=n;i++)
		{
			for(int j=weight[i];j<=m;j++)//此处顺序
			{
				dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);
			}
		}

多重背包

有n件物品和容量为m的背包 给出i件物品的重量以及价值 还有数量 求解让装入背包的物品重量不超过背包容量 且价值最大
特点 它与完全背包有类似点 特点是每个物品都有了一定的数量
设dp[j]表示重量不超过i公斤的最大价值 可得出状态转移方程为:dp[j]=max{dp[j],dp[j−k∗weight[i]]+k∗value[i]}

		for(int i=1;i<=n;i++)
		{
			for(int j=m;j>=weight[i];j--)//此处逆序
			{
				for(int k=1;k<=t[i];k++)//t[i]为每件物品的数量
				{	
					if(j-k*weight[i]<0)
						break;
					dp[j]=max(dp[j],dp[j-k*weight[i]]+k*value[i]);
				}
			}
		}

但是这种情况可能会超时,大部分情况下我们把多重背包分解为01背包与完全背包

如例题:
Whuacmers use coins.They have coins of value A1,A2,A3…An Silverland dollar. One day Hibix opened purse and found there were some coins. He decided to buy a very nice watch in a nearby shop. He wanted to pay the exact price(without change) and he known the price would not more than m.But he didn’t know the exact price of the watch.

You are to write a program which reads n,m,A1,A2,A3...An and C1,C2,C3...Cn corresponding to the number of Tony's coins of value A1,A2,A3...An then calculate how many prices(form 1 to m) Tony can pay use these coins. 

Input
The input contains several test cases. The first line of each test case contains two integers n(1 ≤ n ≤ 100),m(m ≤ 100000).The second line contains 2n integers, denoting A1,A2,A3…An,C1,C2,C3…Cn (1 ≤ Ai ≤ 100000,1 ≤ Ci ≤ 1000). The last test case is followed by two zeros.
Output
For each test case output the answer on a single line.
Sample Input

3 10
1 2 4 2 1 1
2 5
1 4 2 1
0 0

Sample Output

8
4

题意:Whuacmers有n中硬币,价值分别为A1,A2,A3…An ,每种硬币的个数为C1,C2,C3…Cn,要求的是从1到m,这些硬币可以凑成多少种价值,即有多少个dp[i]==i; dp[i]表示当背包装钱容量为i时最大能在背包里装多少钱

AC代码:

#include<iostream>
#include<algorithm>
#include<string.h>
#include<cstdio>
using namespace std;
int m;
int v[1005];
int num[1005];
int dp[100005];// dp[i]表示当背包装钱容量为i时最大能在背包里装多少钱 
void Zero(int cost)//01背包 
{
	for(int i=m;i>=cost;i--)
		dp[i]=max(dp[i],dp[i-cost]+cost);
}
void Com(int cost)//完全背包 
{
	for(int i=cost;i<=m;i++)
		dp[i]=max(dp[i],dp[i-cost]+cost);
}
void mul(int v,int num)
{
	if(v*num>=m)//如果这种价值的硬币*数量 >= 要支付的最大钱数 ,那么就相当于完全背包,钱数不超过m情况下可以一直取 
		Com(v);
	else//否则就将多重背包转换为01背包 
	{
		int k=1;
		while(k<=num)
		{
			Zero(k*v);
			num-=k;
			k*=2;//采用二进制思想  因为从二进制数最小开始1 2 4....可以组合为任何数 如若是5个1块钱的话不用二进制优化01背包的话要循环5次,用了二进制只需循环3次,依次进去1 2 4 
		}
		Zero(v*num);
	}
}
int main()
{
	int n;
	while(scanf("%d%d",&n,&m))
	{
		if(n==0&&m==0) break;
		memset(dp,0,sizeof(dp));
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&v[i]);//输入每个硬币的价值 
		}
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&num[i]);//输入每个硬币价值的数量,此题每个硬币重量与价值相等 
		}
		for(int i=1;i<=n;i++)//外循环,每次进去一种硬币
			mul(v[i],num[i]);//进入函数判断是转换为01背包,还是完全背包 
		int ans=0;
		for(int i=1;i<=m;i++)
		{
			if(dp[i]==i)//当dp[i]==i时,说明可以凑够支付i元,i是<=m的,就是一种情况 
				ans++;
		} 
		printf("%d\n",ans);
	}
	return 0;
}

主要是要注意二进制思想优化,如若是将多重背包转换为01背包的话,假设有5个价值为1的硬币,那么如果转化为01背包,就相当于5个物品,需要循环五次可得到dp[1]~dp[5]。但用二进制思想1 2 4循环3次就得到了一样的dp[1]~dp[5] 主要是因为,任何数都能由二进制表示的数相加组成!!!

分组背包

分组背包是LC(01)背包的一种变形。分组背包中物品被分成几组,每组中只能挑选出一件物品(或者不拿)加入背包,这是与01背包的区别。
在01背包中,我们以每一件物品作为动态规划的每一阶段,但是在分组背包中我们要以每一组作为每一阶段。
其实很简单,代码如下。

//f[i]表示背包容量为i时能获得的最大价值
for(int i=1;i<=k;i++)//一共k个组,遍历每一组
{
	for(int c=v;c>=0;c--)//枚举背包容量
	{
	 	for( each 物品j in 第i组 )//遍历每一组中每一个物品的容量
		{
			if(c>=w[j])
				f[c]=max(f[c],f[c-w[j]+v[j]]);
		}
	}
}

例题:
ACboy has N courses this term, and he plans to spend at most M days on study.Of course,the profit he will gain from different course depending on the days he spend on it.How to arrange the M days for the N courses to maximize the profit?
Input
The input consists of multiple data sets. A data set starts with a line containing two positive integers N and M, N is the number of courses, M is the days ACboy has.
Next follow a matrix A[i][j], (1<=i<=N<=100,1<=j<=M<=100).A[i][j] indicates if ACboy spend j days on ith course he will get profit of value A[i][j].
N = 0 and M = 0 ends the input.
Output
For each data set, your program should output a line which contains the number of the max profit ACboy will gain.
Sample Input

2 2
1 2
1 3
2 2
2 1
2 1
2 3
3 2 1
3 2 1
0 0

Sample Output

3
4
6

AC代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdio> 
using namespace std;
int dp[1005];//dp[i]表示天数为i时能得到的最大利润 
int main()
{
	int a[105][105]; 
	int n,m;
	while(scanf("%d%d",&n,&m))
	{
		if(n==0&&m==0) break;
		memset(dp,0,sizeof(dp));
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=m;j++)
			{
				scanf("%d",&a[i][j]);//完成课程i花费j天所获得的利润
			}
		}
		for(int i=1;i<=n;i++)//每一个课程相当于每一组,对每一组遍历 
		{
			for(int j=m;j>=0;j--)//花费的天数,由于对每个课程可选可不选,要求的是利润最大。因此j>=0 
			{
				for(int k=1;k<=m;k++)//对每一组中的每一个物品消耗的天数遍历,由于在此题中是n*m二维数组形式,因此遍历1~m
				{
					if(j>=k)
					dp[j]=max(dp[j],dp[j-k]+a[i][k]);
				} 
			} 
		} 
		printf("%d\n",dp[m]);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

henulmh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值