动态规划-背包问题

1、01背包

1267:【例9.11】01背包问题


时间限制: 1000 ms         内存限制: 65536 KB
提交数: 12555     通过数: 7735

【题目描述】

一个旅行者有一个最多能装 MM 公斤的背包,现在有 nn 件物品,它们的重量分别是W1W2...,WnW1,W2,...,Wn,它们的价值分别为C1,C2,...,CnC1,C2,...,Cn,求旅行者能获得最大总价值。

【输入】

第一行:两个整数,MM(背包容量,M200M≤200)和NN(物品数量,N30N≤30);

2..N+12..N+1行:每行二个整数WiCiWi,Ci,表示每个物品的重量和价值。

【输出】

仅一行,一个数,表示最大总价值。

【输入样例】

10 4
2 1
3 3
4 5
7 9

【输出样例】

12
我们先分析一下、面对每个物品,我们只有选择拿取或者不拿两种选择,不能选择装入某物品的一部分,也不能装入同一物品多次。
所以根据这个我们得出状态转移方程
b[j]=max(b[j]//不拿,b[j-w[i]]+c[i]//拿)
所以很轻易写出代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int w[201],c[201],b[201],m,n;
int main()
{
    cin>>m>>n;
    for(int i=1;i<=n;i++)
        cin>>w[i]>>c[i];
    for(int i=1;i<=n;i++)
        for(int j=m;j>=w[i];j--)//防止重复选取从后往前循环
        b[j]=max(b[j],b[j-w[i]]+c[i]);
    cout<<b[m];
}

2、完全背包

1268:【例9.12】完全背包问题


时间限制: 1000 ms         内存限制: 65536 KB
提交数: 10454     通过数: 5617

【题目描述】

设有nn种物品,每种物品有一个重量及一个价值。但每种物品的数量是无限的,同时有一个背包,最大载重量为MM,今从nn种物品中选取若干件(同一种物品可以多次选取),使其重量的和小于等于MM,而价值的和为最大。

【输入】

第一行:两个整数,MM(背包容量,M200M≤200)和NN(物品数量,N30N≤30);

2..N+12..N+1行:每行二个整数Wi,CiWi,Ci,表示每个物品的重量和价值。

【输出】

仅一行,一个数,表示最大总价值。

【输入样例】

10 4
2 1
3 3
4 5
7 9

【输出样例】

max=12
这个与01背包的的不同就是可以多次选取……
所以
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int w[201],c[201],b[201],m,n;
int main()
{
    cin>>m>>n;
    for(int i=1;i<=n;i++)
        cin>>w[i]>>c[i];
    for(int i=1;i<=n;i++)
        for(int j=w[i];j<=m;j++)
        b[j]=max(b[j],b[j-w[i]]+c[i]);
    cout<<"max="<<b[m];
}

3、多重背包

就是每种物品有一个选取的上限所以我们可以转化为选0-k件的01背包

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int w[6001],c[6001],s[6001],b[6001],m,n;
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>w[i]>>c[i]>>s[i];
    for(int i=1;i<=n;i++)
        for(int j=m;j>=0;j--)
            for(int k=0;k<=s[i];k++)
            {    
                if(j-k*w[i]<0) break;
                b[j]=max(b[j],b[j-k*w[i]]+k*c[i]);
            }
            
    cout<<b[m];
}

4、混合背包

我们可以把01背包部分看做选择最高1件,和多重背包一起做。

然后再做完全背包部分

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int w[6001],c[6001],s[6001],b[6001],m,n;
int main()
{
    cin>>m>>n;
    for(int i=1;i<=n;i++)
        cin>>w[i]>>c[i]>>s[i];
    for(int i=1;i<=n;i++)
    {
        if(s[i]==0){
            for(int j=w[i];j<=m;j++)
                b[j]=max(b[j],b[j-w[i]]+c[i]);
        }
        else{
            for(int k=1;k<=s[i];k++)
                for(int j=m;j>=w[i];j--)
                    b[j]=max(b[j],b[j-w[i]]+c[i]);
        }
    }
    cout<<b[m];
}

5、二维费用的背包

二维费用的背包问题是指:对于每件物品,具有两种不同的费用;选择这件物品必须同时付出这两种代价;对于每种代价都有一个可付出的最大值(背包容量)。问怎样选择物品可以得到最大的价值。设这两种代价分别为代价1和代价2,第i件物品所需的两种代价分别为a[i]和b[i]。两种代价可付出的最大值(两种背包容量)分别为V和U。物品的价值为c[i]。

而对于这个问题,我们把b数组加上1维,加上一个费用状态就可以解决。

1271:【例9.15】潜水员


时间限制: 1000 ms         内存限制: 65536 KB
提交数: 4538     通过数: 2614

【题目描述】

潜水员为了潜水要使用特殊的装备。他有一个带2种气体的气缸:一个为氧气,一个为氮气。让潜水员下潜的深度需要各种的数量的氧和氮。潜水员有一定数量的气缸。每个气缸都有重量和气体容量。潜水员为了完成他的工作需要特定数量的氧和氮。他完成工作所需气缸的总重的最低限度的是多少?

例如:潜水员有5个气缸。每行三个数字为:氧,氮的(升)量和气缸的重量:

3 36 120

10 25 129

5 50 250

1 45 130

4 20 119

如果潜水员需要5升的氧和60升的氮则总重最小为249(1,2或者4,5号气缸)。

你的任务就是计算潜水员为了完成他的工作需要的气缸的重量的最低值。

【输入】

第一行有2整数m,n(1≤m≤21,1≤n≤79)。它们表示氧,氮各自需要的量。

第二行为整数k(1≤n≤1000)表示气缸的个数。

此后的k行,每行包括aibici1ai211bi791ci8003ai,bi,ci(1≤ai≤21,1≤bi≤79,1≤ci≤800)3整数。这些各自是:第i个气缸里的氧和氮的容量及汽缸重量。

【输出】

仅一行包含一个整数,为潜水员完成工作所需的气缸的重量总和的最低值。

【输入样例】

5 60
5
3 36 120
10 25 129
5 50 250
1 45 130
4 20 119

【输出样例】

249
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 1001 
using namespace std;
int w[N],c[N],s[N],b[N][N],m1,m2,n;
int main()
{    
    memset(b,127,sizeof(b));
    b[0][0] = 0;
    cin>>m1>>m2>>n;
    for(int i=1;i<=n;i++)
        cin>>s[i]>>c[i]>>w[i];
    for(int i=1;i<=n;i++)
        for(int j=m1;j>=0;j--)
            for(int k=m2;k>=0;k--)
            {
                int t1=j+s[i],t2=k+c[i];
                if (t1>m1)
                    t1=m1;
                if (t2>m2)
                    t2=m2;
                if (b[t1][t2]>b[j][k]+w[i])
                    b[t1][t2]=b[j][k]+w[i];
            }
                
    cout<<b[m1][m2];
}
6、分组背包

1272:【例9.16】分组背包


时间限制: 1000 ms         内存限制: 65536 KB
提交数: 3648     通过数: 2306

【题目描述】


一个旅行者有一个最多能装V公斤的背包,现在有n件物品,它们的重量分别是W1W2...,WnW1,W2,...,Wn,它们的价值分别为C1,C2,...,CnC1,C2,...,Cn。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。


【输入】


第一行:三个整数,V(背包容量,V≤200),N(物品数量,N≤30)和T(最大组号,T≤10);


第2..N+1行:每行三个整数Wi,Ci,PWi,Ci,P,表示每个物品的重量,价值,所属组号。


【输出】


仅一行,一个数,表示最大总价值。


【输入样例】


10 6 3
2 1 1
3 3 1
4 8 2
6 9 2
2 8 3
3 9 3

【输出样例】


20
我们把相互冲突的划分为一组,每组中只选一个即可解决,然后我们开个二维数组用于存储第i组的第j个 然后和01背包类似的办法求解。
#include<cstdio>
using namespace std;
int v, n, t;
int w[31], c[31];
int a[11][32], f[201];
int p;
int main() {
    scanf("%d%d%d",&v,&n,&t);
    for (int i = 1; i <= n; i++) {
            scanf("%d%d%d",&w[i],&c[i],&p);
            a[p][++a[p][0]] = i;
    }
    for (int k = 1; k <= t; k++)
        for (int j = v; j >= 0; j--)
            for (int i = 1; i <= a[k][0]; i++)
                if (j >= w[a[k][i]]) {
                    int tmp = a[k][i];
                    if (f[j] < f[j-w[tmp]]+c[tmp])
                        f[j] = f[j-w[tmp]]+c[tmp];
                }
    printf("%d",f[v]);
    return 0;
}
 
 
 
原文地址:https://www.cnblogs.com/zhaoxuelin/p/12726486.html