一个背包总容量为V, 如今有N个物品, 第i个物品容量为weight[i], 价值为value[i], 如今往背包里面装东西, 怎样装才能使背包内物品总价值最大.主要分为3类:ios
整体的,又分为背包恰好装满,与背包没有装满两种状况less
每种物品都只有1个,只有选择与不选择两种状态
有N件物品和一个容量为V的背包,每种物品只有一件,能够选择放或不放。第i件物品的重量是w[i],价值是v[i]。求解将哪些物品装入背包可以使这些物品的重量总和不超过背包容量,且价值总和最大。ide
对于任何只存在两种状态的问题均可以转化为01背包问题测试
定义状态dp[i][v]表示前i件物品恰放入一个容量为v的背包能够得到的最大价值。
状态转移方程:
dp[i][v]=max(dp[i-1][v], dp[i-1][v-w[i]]+v[i])
优化
`dp[v]=max(dp[v],dp[v-w[i]]+v[i])`
空间复杂度O(NW)或者O(W),时间复杂度为O(nW)this
for(int i=0;i<n;i++){ for(int j=背包大小;j>=w[i];j--){ dp[j]=max(dp[j],dp[j-w[i]]+v[i]); } }
分析:
首先,大循环从0到n是指n个物体
其次,内循环是从背包大小到当前物体的重量,是为了减小时间复杂度而这么弄的
对于w[i]是物体的重量,若是剩余背包的大小小于w[i]了,就无需再循环,因此一个端点在w[i]
而j的含义是当前的背包总容量,而容量不可能大于背包大小,因此另外一个大小就在背包大小
若是是升序,那么就会对dp[j-w[i]]进行操做,也就意味着,若是j是w[i]的倍数,那么v[i]就会不断地加进去,也就是彻底背包问题了
而对于降序的话,也就保证了每次第j个物品都只能被放入1此,而不是屡次,就是01背包了idea
每种物品最多有n件可用
有N种物品和一个容量为V的背包。第i种物品最多有n件可用,每件体积是c,价值是w。求解将哪些物品装入背包可以使这些物品的体积总和不超过背包容量,且价值总和最大。spa
对于多重背包问题,能够转化为01背包问题,好比2个价值为5,重量为2的物品,能够转化为a和b两个物品,一个价值为5,重量为2,一个价值也为5,重量也为2code
状态转移方程:
dp[i][j]=max(dp[i-1][j],dp[i-1][j-k*w[i]]+k*v[i]) 其中0<=k<=c[i]
ip
`dp[j]=max(dp[j],dp[j-k*w[i]]+k*v[i])`
k是每种物品放的数量
for(int i=1;i<=n;i++) for(int j=m;j>=0;j--) for(int k=1;k<=c[i];k++){ dp[j]=max(dp[j],dp[j-w[i]]+v[i]); } } }
每种物品都有无限件
有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的体积是c,价值是w。将哪些物品装入背包可以使这些物品的体积总和不超过背包容量,且价值总和最大。
状态转移方程:
dp[i][j]=max(dp[i][j],dp[i-1][v-k*w[i]]+k*v[i]) 其中0<=k*w[i]<=背包大小
`dp[j] = max(dp[j], dp[j - w[i]] + v[i]);`
分析见01背包物体,和01背包同样,就是内循环的循环方向不一样而已,一个升,一个降
空间杂度为O(NW)或O(W),时间复杂度为O(NW)
代码
for (int i = 1; i <= n; i++) { for (int j = w[i]; j <= 背包大小; j++) { dp[j] = max(dp[j], dp[j - w[i]] + v[i]); } }
将3种背包进行混用,若是对于有些物品最多只能选一次,有些能够无限选
则利用01背包和彻底背包的一行代码不一样,进行判断
for(int i=0;i<n;i++){ if(第i个物品是01背包){ for(int j=背包大小;j>=w[i];j--){ dp[j]=max(dp[j],dp[j-w[i]]+v[i]); } }else if(第i个物品是彻底背包){ for (int j = w[i]; j <= 背包大小; j++) { dp[j] = max(dp[j], dp[j - w[i]] + v[i]); } } }
若是再加上多重背包的话
背包混用伪代码
for i= 1 to n if 第i件物品属于01背包 ZeroOnePack(dp,wi,vi) else if 第i件物品属于彻底背包 CompletePack(dp,wi,vi) else if 第i件物品属于多重背包 MultiplePack(dp,wi,vi,ni)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> const int N=1000000; int v[N],w[N]; int dp[N]; void ZeroOnePack(int i){ for(int j=背包大小;j>=w[i];j--){ dp[j]=max(dp[j],dp[j-w[i]]+v[i]); } } void CompletePack(int i){ for(int j=w[i];j<=背包大小;j++){ dp[j]=max(dp[j],dp[j-w[i]]+v[i]); } } void MultiplePack(int i){ for(int j=m;j>=0;j--) for(int k=1;k<=c[i];k++){ dp[j]=max(dp[j],dp[j-w[i]]+v[i]); } } } int main(){ memste(dp,0,sizeof(dp)); for(int i=0;i<n;i++){ if(第i个物品是01背包问题){ ZeroOnePack(i); }else if(第i个物品是彻底背包问题){ CompletePack(i); }else if(第i个物品是多重背包问题){ MultiplePack(i); } }
彻底背包问题
HUD1248
Problem Description
不死族的巫妖王发工资拉,死亡骑士拿到一张N元的钞票(记住,只有一张钞票),为了防止本身在战斗中频繁的死掉,他决定给本身买一些道具,因而他来到了地精商店前.
死亡骑士:"我要买道具!"
地精商人:"咱们这里有三种道具,血瓶150块一个,魔法药200块一个,无敌药水350块一个."
死亡骑士:"好的,给我一个血瓶."
说完他掏出那张N元的大钞递给地精商人.
地精商人:"我忘了提醒你了,咱们这里没有找客人钱的习惯的,多的钱咱们都当小费收了的,嘿嘿."
死亡骑士:"......"
死亡骑士想,与其把钱当小费送个他还不如本身多买一点道具,反正之后都要买的,早点买了放在家里也好,可是要尽可能少让他赚小费.
如今死亡骑士但愿你能帮他计算一下,最少他要给地精商人多少小费.
Input
输入数据的第一行是一个整数T(1<=T<=100),表明测试数据的数量.而后是T行测试数据,每一个测试数据只包含一个正整数N(1<=N<=10000),N表明死亡骑士手中钞票的面值.
注意:地精商店只有题中描述的三种道具.
Output
对于每组测试数据,请你输出死亡骑士最少要浪费多少钱给地精商人做为小费.
Sample Input
2
900
250
Sample Output
0
50
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; int w[]={150,200,350}; int v[]={150,200,350}; int dp[10005]; int main(){ int t; cin>>t; while(t--){ int n; scanf("%d",&n); memset(dp,0,sizeof(dp)); for(int i=0;i<3;i++){ for(int j=w[i];j<=n;j++){ dp[j] = max(dp[j], dp[j - w[i]] + v[i]); } } printf("%d\n",n-dp[n]); } return 0; }
彻底背包问题
Problem Description:
Before ACM can do anything, a budget must be prepared and the necessary financial support obtained. The main income for this action comes from Irreversibly Bound Money (IBM). The idea behind is simple. Whenever some ACM member has any small money, he takes all the coins and throws them into a piggy-bank. You know that this process is irreversible, the coins cannot be removed without breaking the pig. After a sufficiently long time, there should be enough cash in the piggy-bank to pay everything that needs to be paid.
But there is a big problem with piggy-banks. It is not possible to determine how much money is inside. So we might break the pig into pieces only to find out that there is not enough money. Clearly, we want to avoid this unpleasant situation. The only possibility is to weigh the piggy-bank and try to guess how many coins are inside. Assume that we are able to determine the weight of the pig exactly and that we know the weights of all coins of a given currency. Then there is some minimum amount of money in the piggy-bank that we can guarantee. Your task is to find out this worst case and determine the minimum amount of cash inside the piggy-bank. We need your help. No more prematurely broken pigs!
Input
The input consists of T test cases. The number of them (T) is given on the first line of the input file. Each test case begins with a line containing two integers E and F. They indicate the weight of an empty pig and of the pig filled with coins. Both weights are given in grams. No pig will weigh more than 10 kg, that means 1 <= E <= F <= 10000. On the second line of each test case, there is an integer number N (1 <= N <= 500) that gives the number of various coins used in the given currency. Following this are exactly N lines, each specifying one coin type. These lines contain two integers each, Pand W (1 <= P <= 50000, 1 <= W <=10000). P is the value of the coin in monetary units, W is it's weight in grams.
Output
Print exactly one line of output for each test case. The line must contain the sentence "The minimum amount of money in the piggy-bank is X." where X is the minimum amount of money that can be achieved using coins with the given total weight. If the weight cannot be reached exactly, print a line "This is impossible.".
Sample Input
3
10 110
2
1 1
30 50
10 110
2
1 1
50 30
1 6
2
10 3
20 4
Sample Output
The minimum amount of money in the piggy-bank is 60.
The minimum amount of money in the piggy-bank is 100.
This is impossible.
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const long long INF=500005,N=10005; int w[N]; int v[N]; int dp[N]; int main(){ int t; cin>>t; while(t--){ int e,f; scanf("%d%d",&e,&f); int n; cin>>n; int W=f-e; for(int i=0;i<n;i++){ scanf("%d%d",&v[i],&w[i]); } //求最小值,因此对全部的dp进行赋值大数字,但除了第一个数,由于对全部赋值的话,最小值就是大数 for(int i=1;i<=W;i++){ dp[i]=INF; } for(int i=0;i<n;i++){ for(int j=w[i];j<=W;j++){ dp[j]=min(dp[j],dp[j-w[i]]+v[i]); } } if(dp[W]==INF){ printf("This is impossible.\n"); }else{ printf("The minimum amount of money in the piggy-bank is %d.\n",dp[W]); } } return 0; }
几率dp的01背包问题
HDU1203
Problem Description
Speakless很早就想出国,如今他已经考完了全部须要的考试,准备了全部要准备的材料,因而,便须要去申请学校了。要申请国外的任何大学,你都要交纳必定的申请费用,这但是很惊人的。Speakless没有多少钱,总共只攒了n万美圆。他将在m个学校中选择若干的(固然要在他的经济承受范围内)。每一个学校都有不一样的申请费用a(万美圆),而且Speakless估计了他获得这个学校offer的可能性b。不一样学校之间是否获得offer不会互相影响。“I NEED A OFFER”,他大叫一声。帮帮这个可怜的人吧,帮助他计算一下,他能够收到至少一份offer的最大几率。(若是Speakless选择了多个学校,获得任意一个学校的offer均可以)。
Input
输入有若干组数据,每组数据的第一行有两个正整数n,m(0<=n<=10000,0<=m<=10000)
后面的m行,每行都有两个数据ai(整型),bi(实型)分别表示第i个学校的申请费用和可能拿到offer的几率。
输入的最后有两个0。
Output
每组数据都对应一个输出,表示Speakless可能获得至少一份offer的最大几率。用百分数表示,精确到小数点后一位。
Sample Input
10 3
4 0.1
4 0.2
5 0.3
0 0
Sample Output
44.0%
Hint
You should use printf("%%") to print a '%'.
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=100000+5; int v[N]; float p[N],dp[N]; int main(){ int n,m;//背包大小和学校数目 while(scanf("%d%d",&n,&m),(n+m)!=0){ for(int i=0;i<m;i++){ scanf("%d%f",&v[i],&p[i]); } memset(dp,0,sizeof(dp)); for(int i=0;i<m;i++){ for(int j=n;j>=v[i];j--){ dp[j]=max(dp[j],1-(1-dp[j-v[i]])*(1-p[i])); } } printf("%.1f%%\n",dp[n]*100); } return 0; }
多重背包问题HDU2191,此方法时间复杂度为O(NCV)
Problem Description
如今假设你一共有资金n元,而市场有m种大米,每种大米都是袋装产品,其价格不等,而且只能整袋购买。
请问:你用有限的资金最多能采购多少公斤粮食呢?
Input
输入数据首先包含一个正整数C,表示有C组测试用例,每组测试用例的第一行是两个整数n和m(1<=n<=100, 1<=m<=100),分别表示经费的金额和大米的种类,而后是m行数据,每行包含3个数p,h和c(1<=p<=20,1<=h<=200,1<=c<=20),分别表示每袋的价格、每袋的重量以及对应种类大米的袋数。
Output
对于每组测试数据,请输出可以购买大米的最多重量,你能够假设经费买不光全部的大米,而且经费你能够不用完。每一个实例的输出占一行。
Sample Input
1
8 2
2 100 4
4 100 2
Sample Output
400
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int main(){ int t; cin>>t; while(t--){ int n,m; scanf("%d%d",&n,&m); int w[105],v[105],c[105]; int dp[205]; memset(dp,0,sizeof(dp)); for(int i=0;i<m;i++){ scanf("%d%d%d",&v[i],&w[i],&c[i]); } for(int i=0;i<m;i++){ for(int k=1;k<=c[i];k++){ for(int j=n;j>=v[i];j--){ dp[j]=max(dp[j],dp[j-v[i]]+w[i]); } } } printf("%d\n",dp[n]); } return 0; }
二进制优化时间不会。。。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,m; int w[105],v[105],c[105]; int dp[205];//dp[i]指的是购买价值为i的大米时的总重量 //v为当前的价值,w为当前的重量 void zeroonebag(int v,int w){ for(int j=n;j>=v;j--){ dp[j]=max(dp[j],dp[j-v]+w); } } void completebag(int v,int w){ for(int j=v;j<=n;j++){ dp[j]=max(dp[j],dp[j-v]+w); } } void multiple(int v,int w,int c){ //价值大于拥有的价值,则是彻底背包问题 if(c*v>=n){ completebag(v,w); return; } //01背包 int k=1; while(k<=c){ zeroonebag(k*v,k*w); c=c-k; k=k*2; } zeroonebag(c*v,c*w); } int main(){ int t; cin>>t; while(t--){ scanf("%d%d",&n,&m); memset(dp,0,sizeof(dp)); for(int i=0;i<m;i++){ scanf("%d%d%d",&v[i],&w[i],&c[i]); } for(int i=0;i<m;i++){ multiple(v[i],w[i],c[i]); } printf("%d\n",dp[n]); } return 0; }