0
点赞
收藏
分享

微信扫一扫

动态规划-背包问题

Python芸芸 2022-02-04 阅读 44

文章目录

一、01背包

1.01背包引入

是背包中最简单的问题。
01背包的约束条件是给定几种物品,每种物品有且只有一个,并且有权值和体积两个属性。在01背包问题中,因为每种物品只有一个,对于每个问题只需要考虑选与不选两种情况。如果不选将其放入背包中,则不需要处理。如果选择将其放入背包中,由于不清楚之前收入的物品占据了多大的空间,需要枚举将这个物品放入背包后可能占据背包空间的所有情况。

2.01背包例题

例题1:现有四个物品,小偷背包总容量为8,怎么可以偷得价值最多的物品?如:物品编号:1 2 3 4物品重量:2 3 4 5物品价值:3 4 5 8记f(k,w):当背包容量为w,现有k件物品可以偷,所能偷到最大价值

 #include<cstdio>  
 #include <iostream>  
 #include<cstdlib>  
 #include<algorithm>  
 #include <cstring>  
 using namespace std;  
 int f[5][9] = {0};  
 int w[5] ={0,2,3,4,5};  
 int v[5] ={0,3,4,5,8};  
 int main(){  
     int i,j;  
     memset(f,0,sizeof(f));  
     for(int i = 1; i < 5; i++){  
        for(int j = 1; j < 9; j++){  
            if(w[i] > j){  
                f[i][j] = f[i - 1][j];  
            }else{  
                 f[i][j] = max(f[i - 1][j],f[i - 1][j - w[i]] + v[i]);  
             }  
        }  
     }  
    for(int i = 0; i < 5; i++){  
         for(int j = 0; j < 9; j++){  
             cout <<"f["<< i << "]" <<"[" << j<<"]= " << f[i][j] << endl;  
         }  
     }  
    
 } 

3.01背包空间优化

滚动数组:根据后无效性原则,下一状态只与上一个状态有关,可以压缩成一维数组。每运行一遍就让让dp数组刷新一遍;注意求新的dp数组时必须倒着,背包剩余容量要从容量最大值开始,不然物体的小容量会被这个物品的小容量值覆盖。比如dp[8] = max(dp[8],dp[4] + 6)此时这一层的dp[4]还未更新还为上一层的,直接使用即可,如果正着推,此时dp[4]已经覆盖为这一层的,无法正确求出后面的;

辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”

输入格式
第一行有 22 个整数 TT(1 \le T \le 10001≤T≤1000)和 MM(1 \le M \le 1001≤M≤100),用一个空格隔开,TT 代表总共能够用来采药的时间,MM 代表山洞里的草药的数目。

接下来的 MM 行每行包括两个在 11 到 100100 之间(包括 11 和 100100)的整数,分别表示采摘某株草药的时间和这株草药的价值。

输出格式
输出在规定的时间内可以采到的草药的最大总价值。

如果你是辰辰,你能完成这个任务吗?
1).未进行压缩的动态规划

#include<iostream>
#include<algorithm>
using namespace std;
const int n = 10001;
int dp[n][n];
int t[n];
int v[n];
int main(){
	int T,m,i,j;
	cin >> T >> m;
	for(i = 1; i <= m; i++){
		cin >> t[i] >> v[i];
    }
    for(i = 1; i <= m; i++){
		for(j = 1; j <= T; j++){
			if(t[i] > j){
				dp[i][j] = dp[i - 1][j];
			}else{
				dp[i][j] = max(dp[i - 1][j],dp[i - 1][j - t[i]] + v[i]);
			}
		} 
	}
	cout << dp[m][T];
} 

2).压缩后的动态规划(利用滚动数组)

#include<bits/stdc++.h>
using namespace std;
const int n = 10001;
int dp[n];
int t[n];
int v[n];
int main(){
	int T,m;
	cin >> T >>m;
	for(int i = 1; i <= m; i++){
		cin >> t[i] >> v[i];
	}
	for(int i = 1; i <= m; i++){
		for(int j = T; j >= 1; j--){  //递推j必须倒着循环,否则会被覆盖
			if(j >= t[i]){
				dp[j] = max(dp[j],dp[j - t[i]] + v[i]);
			}
		}
	}
	cout << dp[T];
}

输入
开始输入一个数t,表示有t组测试数据。每个测试数据开始输入两个数n和v,表示骨头的总数和袋子的体积。接下一行输入n个数,表示n个骨头的价值。最后一行输入n个数,表示n个骨头的体积。
输出
每个测试数据输出一个数,表示最大的价值。
样例输入
1
5 10
1 2 3 4 5
5 4 3 2 1
样例输出:14

#include <iostream>  
#include <cstring>  
#include <cmath>  
using namespace std;  
const int n = 1000;  
int dp[n][n];  
int val[n];  
int vo[n];  
int main()  
{  
     int t;  
     cin >> t;  
     while (t--)  
     {  
         int n, v;  
         memset(dp, 0, sizeof(dp));  
         cin >> n >> v;  
         for (int i = 1; i <= n; i++)  
         {  
             cin >> val[i];  
         }  
         for (int i = 1; i <= n; i++)  
         {  
             cin >> vo[i];  
         }  
         for (int i = 1; i <= n; i++)  
         {  
             for (int j = 0; j <= v; j++)  
             {  
                 if (vo[i] > j)  
                 {  
                     dp[i][j] = dp[i - 1][j]; //放不下  
                 }  
                 else  
                 {  
                    dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - vo[i]] + val[i]); //将第i件物品放入背包之后的总价值  
                 }  
             }  
         }  
         cout << dp[n][v] << endl;  
     }  
 }  

在这里插入图片描述
在这里插入图片描述

二、完全背包

1.完全背包引入

完全背包的问题是:有N种物品和一个容量为V的背包。第i种物品有若干见可用,每件费用是c[i],价值是w[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值量最大。完全背包和01背包问题很像,不同的是完全背包中的物品每件有若干件,而01背包中只有一件。

2.完全背包使用

输入
第一行:两个整数,M(背包容量,M<=200)和N(物品数量,N<=30);
第2,…N+1行:每行两个整数,Wi,Ci表示每个物品的数量和价值
输出
仅一行,一个数,表示最大总价值。
样例输入
10 4
2 1
3 3
4 5
7 9
样例输出
max = 12
法一:(朴素方法)

每个物品可以取012....直到背包装不下(当前背包价值/w[i])
#include<iostream>  
#include<algorithm>  
#include<cmath>  
using namespace std;  
const int n = 10001;  
int c[n];  
int w[n];  
int dp[n];  
int main() {  
    int m,n;  
    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 >= 1; j--){  
            for(int k = 0; k <= j / w[i]; k++){  
                dp[j] = max(dp[j],dp[j - k*w[i]] + k*c[i]);  
            }  
        }  
    }  
    cout << "max = " << dp[m];  
}  

法二、终极算法:

#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int n = 10001;
int c[n];
int w[n];
int dp[n];
int main() {
	int m,n;
	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 = 0; j <= m; j++) { //正向推 
//			if(j >= w[i]) {
//				dp[j] =max(dp[j],dp[j - w[i]] + c[i]);
//			}
//		}
		for(int j = w[i]; j <= m; j++){
			dp[j] =max(dp[j],dp[j - w[i]] + c[i]); //直接从能放的下的时候开始推 
		} 
	}
	cout << "max = " << dp[m];
}

3.完全背包与01背包的区别

在这里插入图片描述

输入
输入包含多组测试用例。每组数据以一个整数n开始,表示每天的食物清单有n种食物。接下来n行,每行两个整数a和b,其中a表示这种食物可以带给她的幸福值(数值越大越幸福),b表示会增加的卡路里量。最后是一个整数m,表示一天增加的卡路里不能超过m。
输出
对每份清单,输出一个整数,即满足卡路里的同时,她可获得的最大幸福值。

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 1e6 + 10;
int n;
int c[maxn];
int f[maxn];
int dp[maxn];
int main(){

	while(cin >> n){
		for(int i = 1; i <= n; i++){
			cin >> f[i] >>c[i];
		}
		int m;
		cin >> m;
		for(int i = 1; i <= n; i++){
			for(int j = c[i]; j <= m; j++){
				dp[j] = max(dp[j],dp[j - c[i]] + f[i]);
			}
		}
		cout << dp[m] << endl; 
	}
}
 
举报

相关推荐

0 条评论