目录
贪心算法:
部分背包:
给定一个最大载重量为M的卡车和N种食品,有食盐、白糖、大米等。已知第i种食品最多 拥有Wi千克,其商品价值为Vi元/千克,编程确定一种装货方案,使得装入背包中的所有物 品总价值最大。
特点:这些物品都是可以任意装下的。
思路:显然单价越高的越先填,到最后只剩下一部分空间再塞满剩余中最贵的物品即可。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<stdlib.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
struct bag{
int m;
int v;
double w;
}b[150];//封装每个物品的价格 重量以及单价
bool cmp(bag x,bag y){
return x.w > y.w;
}
int main(){
int n,t;
cin >> n >> t;
for(int i = 0;i < n;i++){
cin >> b[i].m >> b[i].v;
b[i].w = 1.0*b[i].v/b[i].m;
}
sort(b,b+n,cmp);
double val = 0;
int i;
for(i = 0;i < n;i++){
if(b[i].m > t) break;
t-=b[i].m;val += b[i].v;
}
val +=(b[i].w*t); //需要把最后剩余的空间填满
printf("%.2lf",val);
return 0;
}
动态规划:
0-1背包:
注意事项:
1:需要在j<sp[i]复制上一层的i,为了dp[i+1]位置用到小于sp[i]的时候
2.递推式:dp[i][j]表示前i的物品种花费j时间所能得到的最大价值。dp[i][j]=max(dp[i-1][j],dp[i-1][j-sp[i]]+w[i]) 不选的情况 dp[i-1][j] ,选的情况就是dp[i-1][j-sp[i]]+w[i]也就是前上一个物品腾出来采这种药材的时间,再加上新物品的价格。
二维数组:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<stdlib.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
int w[150];
int sp[150];
int dp[150][1050];
int main(){
int t,m;
cin >> t >> m;
for(int i = 1;i <= m;i++){
cin >> sp[i] >> w[i];
}
for(int i = 1;i <= m;i++){
for(int j = 0;j <= t;j++){
if(j < sp[i]) dp[i][j] = dp[i-1][j];
else dp[i][j] = max(dp[i-1][j],dp[i-1][j-sp[i]]+w[i]);
}
}
for(int i = 0;i <= m;i++){
for(int j = 0;j <= t;j++)
cout << dp[i][j];
cout << endl;
}
cout << dp[m][t] << endl;
return 0;
}
滚动数组:
顾名思义,滚动i进行更新j。dp[j]表示花费j时间所能达到的最大价值
递推式:dp[j]表示花费j时间可以获得的最大价值。dp[[j]=max(dp[j],dp[j-sp[i]]+w[i]);for从后往前遍历j。不能从前往后,当j<sp[i]是不需要变化dp[j],所以for范围从t到sp[i]即可。不选i的情况直接照搬选择i-1即可,也就是dp[j],选i的情况那么就是dp[j-sp[i]]+w[i]腾出来sp[i]时间给w[i]价值的i药品。从后往前能够利用好之前的数据,如果从前往后的话,第一个数据被更新掉,后面如果用到了更新掉的数据就不是只选择一次了,就是选择两次了。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<stdlib.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
int w[150];
int sp[150];
int dp[1050];
int main(){
int t,m;
cin >> t >> m;
for(int i = 1;i <= m;i++){
cin >> sp[i] >> w[i];
}
for(int i = 1;i <= m;i++){
for(int j = t;j >= sp[i];j--)
dp[j] = max(dp[j],dp[j-sp[i]]+w[i]);
}
cout << dp[t] << endl;
return 0;
}
完全背包:
滚动数组:
更改一个地方代码即可,但是数据范围有变 所以数组要开大 并且用long long。
递推式:dp[j]表示花费j时间可以获得的最大价值。dp[j]=max(dp[j],dp[j-sp[i]]+w[i]) 从前往后,这样的话可以更新前面的新数据,从后往前,后面使用前面的,但前面的是i-1物品最优的,未必是前i最优的。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<stdlib.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int maxn = 1e7+50;
const int maxm = 1e4+50;
ll w[maxm];
ll sp[maxm];
ll dp[maxn];
int main(){
ll t,m;
cin >> t >> m;
for(int i = 1;i <= m;i++){
cin >> sp[i] >> w[i];
}
for(int i = 1;i <= m;i++){
for(int j = sp[i];j <= t;j++)
//dp[j] = max(dp[j],dp[j-sp[i]]+w[i]);
dp[j] = max(dp[j],dp[j-sp[i]]+w[i]);
}
cout << dp[t] << endl;
return 0;
}
二维数组数据太大 不变操作。否则也只更改一个地方即可:
for(int i = 1;i <= m;i++){
for(int j = 0;j <= t;j++){
if(j < sp[i]) dp[i][j] = dp[i-1][j];
//else dp[i][j] = max(dp[i-1][j],dp[i-1][j-sp[i]]+w[i]);
else dp[i][j]=max(dp[i-1][j],dp[i][j-sp[i]]+w[i]);
}
}
01背包、完全背包区别:
二维数组:
01背包递推式:dp[i][j] = max(dp[i-1][j],dp[i-1][j-sp[i]]+w[i]);
完全背包递推式:dp[i][j]=max(dp[i-1][j],dp[i][j-sp[i]]+w[i]);
发现,第一个不选i的是一样的 都是dp[i-1][j]表示不选也就是前i-1药品花费j时间。不同的是第二个。01背包中为dp[i-1][j-sp[i]]+w[i]
表示选的话就等于前i-1个空出来sp[i]也就是花费j-sp[i]可以获得最大价值加上w[i]。i-1是因为一个物品只能被选择一次,所以不可以是i,而完全背包是多次选择,所以完全背包应该等于前i个空出来sp[i]也就是j-sp[i]可以获得的最大价值加上w[i]。
滚动数组:
dp[j]=max(dp[j],dp[j-sp[i]]+w[i]),01背包和完全背包的递推式一样,唯一的区别在于for循环的前后顺序。i被省略,也就是i滚动更新dp[j]。因为如果选择i的话应该是dp[j-sp[i]]+w[i]。如果从前往后,每一次更新都是用的最新的数据,也就是前i的物品的最优解,所以是完全背包,如果01背包也这样的话,会出现一个物品多次选择,比如第一个更新后的数据,第二个更新的时候用到了第一个更新后的,那么第二个的最优解就选择了两次i物品。从后往前,每一次用的都是前i-1药品的数据,所以只被选了1次。