【动态规划-背包模型part3(01背包模型、多重背包模型)】:开心的金明、潜水员、庆功会、二维背包的费用问题【已更新完成】

题目类型
开心的金明01背包
潜水员二维限制的01背包
庆功会多重背包
二维背包的费用问题二维限制的01背包

1、开心的金明

金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间。

更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过 N 元钱就行”。

今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的 N 元。

于是,他把每件物品规定了一个重要度,分为 5 等:用整数 1∼5 表示,第 5 等最重要。

他还从因特网上查到了每件物品的价格(都是整数元)。

他希望在不超过 N 元(可以等于 N 元)的前提下,使每件物品的价格与重要度的乘积的总和最大。

设第 j 件物品的价格为 v[j] ,重要度为 w[j] ,共选中了 k 件物品,编号依次为 j1,j2,…,jk ,则所求的总和为:

v[j1]×w[j1]+v[j2]×w[j2]+…+v[jk]×w[jk] 请你帮助金明设计一个满足要求的购物单。

输入格式 输入文件的第 1 行,为两个正整数 N 和 m ,用一个空格隔开。(其中 N 表示总钱数,m 为希望购买物品的个数)

从第 2 行到第 m+1 行,第 j 行给出了编号为 j−1 的物品的基本数据,每行有 2 个非负整数 v 和 p 。(其中 v
表示该物品的价格,p 表示该物品的重要度)

输出格式 输出文件只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(数据保证结果不超过 108)。

数据范围
1≤N<30000,1≤m<25,0≤v≤10000,1≤p≤5
输入样例:

1000 5 800 2 400 5 300 5 400 3 200 2
输出样例:

3900

思路:

01背包问题,不过存储的是v[i]w[i]的乘积的集合,属性是最大值

代码:

#include<bits/stdc++.h>

using namespace std;

const int N=28;

int v[N],w[N];

const int M=30003;

int f[N][M];

//01背包问题,状态集合存储的是w[i]*v[i]的值的和

int main()
{
    int t,m;

    cin>>t>>m;

    for(int i=1;i<=m;i++)
    {
        cin>>v[i]>>w[i];
    }


    for(int i=1;i<=m;i++)//枚举物品个数
        for(int j=0;j<=t;j++)
        {
            f[i][j]=max(f[i][j],f[i-1][j]);

            if(j>=v[i])f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]*v[i]);
        }

    cout<<f[m][t];
    return 0;
}

2、潜水员

潜水员为了潜水要使用特殊的装备。

他有一个带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 。它们表示氧,氮各自需要的量。

第二行为整数 k 表示气缸的个数。

此后的 k 行,每行包括ai,bi,ci ,3个整数。这些各自是:第 i 个气缸里的氧和氮的容量及气缸重量。

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

数据范围
1≤m≤21 , 1≤n≤79 , 1≤k≤1000 , 1≤ai≤21 , 1≤bi≤79 , 1≤ci≤800
输入样例:
5 60
5
3 36 120
10 25 129
5 50 250
1 45 130
4 20 119
输出样例:
249

思路:

01背包问题,只不过说有两个限制条件
注意f[i][j][k]中的j维度和k维度不能为负数(如果说氧气和氮气已经超过所需要的量,那么减去一个氮气罐中的氧气和但其量会导致两个维度的数变成负数,这时候就要和0取max)

f[i][j][k]=f[i][j][k-1];
f[i][j][k]=min(f[i][j][k],f[max(0,i-tool[k].o)][max(0,j-tool[k].n)][k-1]+tool[k].w);//和0取max

代码:

#include<bits/stdc++.h>

using namespace std;

const int N=1003;

int n,m,K;

struct node
{
	int o,n,w;
}tool[N];

int f[81][81][N]; 

int main()
{
	cin>>m>>n;//所需要的氧和氮 
	
	cin>>K;//气缸的个数 
	
	for(int i=1;i<=K;i++)
	{
		scanf("%d%d%d",&tool[i].o,&tool[i].n,&tool[i].w);//读入该气缸的氧气、氮气、重量	
	}
	
	memset(f,0x3f,sizeof f);//求最小值要把所有值初始化为无穷大 
	
	f[0][0][0]=0;
    //f[0][0][1]=0;
	int res=1<<31-1;
	
	for(int k=1;k<=K;k++)//枚举气缸 for(int k=1;k<=K;k++)//枚举气缸 
    	    for(int i=0;i<=m;i++)//枚举氧气 
    		    for(int j=0;j<=n;j++)//枚举氮气 
        		{
        		    
        		        f[i][j][k]=f[i][j][k-1];
            
        				//f[i][j][k]=f[i][j][k-1];//k-1个尚能满足i和j,k个更能满足i和j了 
        				//if(i>=tool[i].o)f[i][j][k]=min(f[i][j][k],f[i-tool[i].o][j][k]+node[i].w);
        				//if(i>=tool[i].n)f[i][j][k]=min(f[i][j][k],f[i][j-tool[i].n][k]+node[i].w);
        				f[i][j][k]=min(f[i][j][k],f[max(0,i-tool[k].o)][max(0,j-tool[k].n)][k-1]+tool[k].w);
        			
        		}
        		
	//cout<<f[m][n][K];
	
	for(int i=1;i<=K;i++)res=min(f[m][n][i],res);
	
	cout<<res;
	
	return 0;
} 
/*
5 60
5
3 36 120
10 25 129
5 50 250
1 45 130
4 20 119

249
*/

3、庆功会

为了庆贺班级在校运动会上取得全校第一名成绩,班主任决定开一场庆功会,为此拨款购买奖品犒劳运动员。

期望拨款金额能购买最大价值的奖品,可以补充他们的精力和体力。

输入格式 第一行二个数n,m,其中n代表希望购买的奖品的种数,m表示拨款金额。

接下来n行,每行3个数,v、w、s,分别表示第I种奖品的价格、价值(价格与价值是不同的概念)和能购买的最大数量(买0件到s件均可)。

输出格式 一行:一个数,表示此次购买能获得的最大的价值(注意!不是价格)。

数据范围
n≤500,m≤6000,v≤100,w≤1000,s≤10
输入样例:
5 1000
80 20 4
40 50 9
30 50 7
40 30 6
20 20 1
输出样例:
1040

思路:

经典多重背包问题,相对于完全背包和01背包,只是加一个维度枚举数量

代码:

#include<bits/stdc++.h>

using namespace std;

const int N=503;

int w[N];

int v[N];

int s[N];

const int M=6003;

long long  f[N][M];

int main()
{
    int n,m;

    scanf("%d%d",&n,&m);

    //多重背包问题,每个物品的个数是有限的

    for(int i=1;i<=n;i++)
    {

        scanf("%d%d%d",&v[i],&w[i],&s[i]);
    }


    for(int i=1;i<=n;i++)//枚举种类
    {

        for(int j=0;j<=m;j++)//枚举体积
        {

            for(int k=0;k<=s[i];k++)//枚举该物品购买的数量,购买的个数必须小于当前物品的数量
            {
                f[i][j]=max(f[i][j],f[i-1][j]);//这句话没必要,因为f[i][j]必然大于f[i][j-1]

                //因为在下面枚举k=0的时候,f[i-1][j-k*v[i]]+k*w[i]==f[i-1][j]
                //则下面的f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i])转化为f[i][j]=max(f[i][j],f[i-1][j])
                //已经包括了:f[i][j]=max(f[i][j],f[i-1][j])这个状态

                if(k*v[i]<=j)f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);

            }
        }
    }


    cout<<f[n][m];
    return 0;
}

4、二位背包的费用问题

有 N 件物品和一个容量是 V 的背包,背包能承受的最大重量是 M 。

每件物品只能用一次。体积是 vi ,重量是 mi ,价值是 wi 。

求解将哪些物品装入背包,可使物品总体积不超过背包容量,总重量不超过背包可承受的最大重量,且价值总和最大。 输出最大价值。

输入格式 第一行三个整数,N,V,M ,用空格隔开,分别表示物品件数、背包容积和背包可承受的最大重量。

接下来有 N 行,每行三个整数 vi,mi,wi ,用空格隔开,分别表示第 i 件物品的体积、重量和价值。

输出格式 输出一个整数,表示最大价值。

数据范围
0<N≤1000

0<V,M≤100

0<vi,mi≤100

0<wi≤1000
输入样例
4 5 6
1 2 3
2 4 4
3 4 5
4 5 6
输出样例:
8

思路:

同样还是二个限制维度,加一层循环,按照01背包问题处理即可

代码:

#include<bits/stdc++.h>
//也还是01背包问题,只不过有二个限制维度,一个价值维度
using namespace std;

//v和m的限制
//目标:使得w最大

//f[i][j][k]表示选前i个物品,体积不超过j,重量不超过k的价值的状态的集合
//属性:最大值

const int N=1003;

const int M=103;

int v[N],m[N];//体积和重量

int w[N];//价值

int f[N][M][M];

int main()
{
    int n,V,M;
    cin>>n>>V>>M;

    for(int i=1;i<=n;i++)
    {
        scanf("%d%d%d",&v[i],&m[i],&w[i]);
    }

    for(int i=1;i<=n;i++)//枚举第i个物品
    {
        for(int j=0;j<=V;j++)
        {
            for(int k=0;k<=M;k++)
            {
                f[i][j][k]=max(f[i][j][k],f[i-1][j][k]);
                if(j>=v[i] && k>=m[i])f[i][j][k]=max(f[i][j][k],f[i-1][j-v[i]][k-m[i]]+w[i]);
            }
        }
    }
    cout<<f[n][V][M];
    return 0;
}

  • 24
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当然,下面是使用01背包算法解决P1060 开心金明问题的代码,并附有详细注释: ```cpp #include <iostream> #include <algorithm> using namespace std; const int MAXN = 33000; // 背包最大容量 int dp[MAXN]; // 动态规划数组,dp[i]表示容量为i时的最大价值 int main() { int n, m; // n表示物品个数,m表示背包容量 cin >> m >> n; for (int i = 0; i < n; i++) { int v, p; // v表示物品的体积,p表示物品的价值 cin >> v >> p; for (int j = m; j >= v; j--) { // 从后往前遍历背包容量,保证之前计算的dp[j-v]没有被覆盖 dp[j] = max(dp[j], dp[j - v] + v * p); // 更新当前容量下的最大价值 } } cout << dp[m] << endl; // 输出背包容量为m时的最大价值 return 0; } ``` 代码解释: 1. 首先,我们定义了常量MAXN表示背包的最大容量,并声明了一个长度为MAXN的dp数组,dp[i]表示容量为i时的最大价值。 2. 接下来,从输入中读取背包容量m和物品个数n。 3. 然后,使用一个循环遍历每个物品。在每次循环中,我们读取当前物品的体积v和价值p。 4. 接着,使用一个逆序的循环遍历背包容量j,从m到v。这样做是为了保证之前计算的dp[j-v]没有被覆盖。 5. 在内层循环中,我们更新dp[j]的值,将其更新为dp[j]和dp[j-v] + v * p的较大值。其中,dp[j]表示不选当前物品时的最大价值,dp[j-v] + v * p表示选择当前物品时的最大价值。 6. 最后,输出dp[m],即背包容量为m时的最大价值。 希望这个解释对你有帮助!如果还有其他问题,请随时提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值