01背包问题——

01背包题目的雏形是:php

有N件物品和一个容量为V的背包。第i件物品的体积是c[i],价值是w[i]。求解将哪些物品装入背包可以使价值总和最大。ios

从这个题目中能够看出,01背包的特色就是:每种物品仅有一件,能够选择放或不放。c++

其状态转移方程是:spa

f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}.net

对于这方方程其实并不难理解,方程之中,如今须要放置的是第i件物品,这件物品的体积是c[i],价值是w[i],所以f[i-1][v]表明的就是不将这件物品放入背包,而f[i-1][v-c[i]]+w[i]则是表明将第i件放入背包以后的总价值,比较二者的价值,得出最大的价值存入如今的背包之中。code

 

题目描述:blog

假设山洞里共有a,b,c,d ,e这5件宝物(不是5种宝物),它们的重量分别是2,2,6,5,4,它们的价值分别是6,3,5,4,6,如今给你个承重为10的背包, 怎么装背包,能够才能带走最多的财富。内存

有编号分别为a,b,c,d,e的五件物品,它们的重量分别是2,2,6,5,4,它们的价值分别是6,3,5,4,6,如今给你个承重为10的背包,如何让背包里装入的物品具备最大的价值总和?ci

    此例题参考博客:动态规划之01背包问题(最易理解的讲解)get

先对表进行大概的说明,前面三列是物品名字,重量和价值。后面1到10表示背包容量。

e3表示背包容量为3,当前物品为e。 装不下

d3表示背包容量为3,当前物品为e,d。装不下

c3表示背包容量为3,当前物品为e,d,c。 装不下

b3表示背包容量为3,当前物品为e,d,c,b 能够装b物品,因此值为b物品的价值3

a3表示背包容量为3,当前物品为e,d,c,b ,a 这时根据状态转移方程,比较b3和b1+a的价值的值。

后者更大,因此a3值为6.

当时看了上面博客的讲解后本身画出了上面这张表,但是比较好奇为何要从最后一件物品开始往里面装,不能从第一件物品直接开始装嘛?

因而再次从第一件物品以一样的填法进行填表,发现其实结果是同样的,并无影响。

这里对e8进行说明,d8为11,当前容量8减去e的重量4=4,因此寻找d4值 为9  9+6=15>11 因此e8的值更新为15

一道例题:有一个容量为m(1<=m<=4000000)的背包,有n(1<=n<=16)个物品,每一个物品有体积v(1<=v<=2012)和价值w(0<=2012),如今要你选择一些物品,使得背包所装物品的总价值最大。

C++代码:

 

#include <iostream>
#include <memory.h>
using namespace std;
struct wupin
{
    int v;//体积
    int w;//价值
}a[10000];
int dp[10000][10000];
int main()
{
    int n,m;
    cin>>m>>n;
    memset(dp,0,sizeof(dp));
    memset(a,0,sizeof(a));
    for(int i=1;i<=n;i++)
        cin>>a[i].v>>a[i].w;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            if(j>=a[i].v)
                dp[i][j]=max(dp[i-1][j],dp[i-1][j-a[i].v]+a[i].w);
            else
                dp[i][j]=dp[i-1][j];
        }
//    for(int i=1;i<=n;i++)
//       {
// for(int j=1;j<=m;j++)
//            cout<<dp[i][j]<<' ';
//            cout<<endl;
//       }
    cout << dp[n][m] << endl;
    return 0;
}

通常写法:

大小为j的背包是否放第i个物品,取决于大小为(j-a[i].v)的背包的最优解+a[i].w 是否大于当前不放这个物品的最优解的价值

 

#include <iostream>
#include <memory.h>
using namespace std;
struct wupin
{
    int v;//体积
    int w;//价值
}a[10000];
int dp[100000];
int main()
{
    int n,m;
    cin>>m>>n;
    memset(dp,0,sizeof(dp));
    memset(a,0,sizeof(a));
    for(int i=0;i<n;i++)
        cin>>a[i].v>>a[i].w;
    for(int i=0;i<n;i++)
        for(int j=m;j>=a[i].v;j--)
            dp[j]=max(dp[j],dp[j-a[i].v]+a[i].w);
    cout << dp[m] << endl;
    return 0;
}

 

最简洁的写法:

 

 

#include <cstdio>
#include <algorithm>
using namespace std;
int f[1005];
int n,m;
int main()
{
    int w,v;
    scanf("%d%d",&m,&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&v,&w);
        for(int j=m;j>=v;j--)
            f[j]=max(f[j],f[j-v]+w);
    }
    printf("%d\n",f[m]);
    return 0;
}

补充:求背包当前容量为m时,共有多少种不一样选择。

例题:

问题 L: 动态规划进阶题目之神奇的口袋

时间限制: 1 Sec  内存限制: 64 MB
提交: 77  解决: 47
[提交][状态][讨论版][命题人:lyh]

题目描述

有一个神奇的口袋,总的容积是40,用这个口袋能够变出一些物品,这些物品的整体积必须是40。John如今有n个想要获得的物品,每一个物品的体积分别是a1,a2……an。John能够从这些物品中选择一些,若是选出的物体的整体积是40,那么利用这个神奇的口袋,John就能够获得这些物品。如今的问题是,John有多少种不一样的选择物品的方式。

输入

输入的第一行是正整数n (1 <= n <= 20),表示不一样的物品的数目。接下来的n行,每行有一个1到40之间的正整数,分别给出a1,a2……an的值。

输出

输出不一样的选择物品的方式的数目。

样例输入

3
20
20
20

样例输出

3
#include<bits/stdc++.h>
using namespace std;
int a[50];
int b[50][50];//b[i][j]表示从i件物品中凑出j体积的方法数
int main() {
	int n;
	cin>>n;
	int cnt=0;
	for(int i=1; i<=n; i++) {
		cin>>a[i];
		b[i][0]=1;
	}
	b[0][0]=1;
	for(int j=1; j<=40; j++)//遍历背包容量 
		for(int i=1; i<=n; i++) {
			b[i][j]=b[i-1][j];
			if(j>=a[i]) {
				b[i][j]+=b[i-1][j-a[i]];
			}
		}
	cout<<b[n][40];
	return 0;
}