1.背包
1.1 01背包
概览
1998年的石溪布鲁克大学算法库的研究表明,在75个算法问题中,背包问题是第18个最受欢迎,第4个最需要解决的问题(前三为后kd树,后缀树和bin包装问题)。
背包问题出现在各种领域的现实世界的决策过程中,例如寻找最少浪费的方式来削减原材料,选择投资和投资组合,选择资产支持资产证券化,和生成密钥为Merkle-Hellman和其他背包密码系统。
背包算法的一个早期应用是在测试的构建和评分中,测试者可以选择他们回答哪些问题。对于小例子来说,这是一个相当简单的过程,为测试者提供这样的选择。例如,如果考试包含12个问题,每个问题的价值为10分,测试者只需回答10个问题即可获得100分的最高分。然而,在点值的异质分布的测试 - 即不同的问题值得不同的点值 - 更难以提供选择。 Feuerman和Weiss提出了一个系统,其中学生被给予一个异质测试,共有125个可能的点。学生被要求尽可能回答所有的问题。在总点数加起来为100的问题的可能子集中,背包算法将确定哪个子集给每个学生最高的可能得分。
思路
1.状态转移方程
假设他的剩余容量为t,已经放入的当前总价值为dp[t],下一个要放入的物品代价是c[i],价值是v[i]
分两种情况:①->维持原状态不变 ②->吸纳这个物品,
所以得到状态转移方程
dp[t] = max(dp[t],dp[t - c[i]] + v[i]);
2.循环次序
外层-物品数
内层:只需对剩余容量位于(c[i],t]进行遍历
重要:对于01背包,一个物品只有一次机会,所以要倒着遍历,从t到c[i]
3.实现
#include<bits/stdc++.h>
#define int long long
using namespace std;
int v[10001],c[10001];
int dp[10005];
signed main(){
int t,m;
scanf("%lld%lld",&t,&m);
for(int i = 1;i <= m;i++){
scanf("%lld%lld",&c[i],&v[i]);
}
for(int i = 1;i <= m;i++){
for(int j = t;j >= c[i];j--){
dp[j] = max(dp[j - c[i]] + v[i],dp[j]);
}
}
printf("%lld",dp[t]);
return 0;
}
1.2 完全背包 :在01背包的基础上,将物品个数改为无限个
状态方程不变
对于完全背包,一个物品有不限机会,所以要正着遍历,从t=c[i]到t
#include<bits/stdc++.h>
#define int long long
using namespace std;
int v[10001],c[10001];
int dp[10005];
signed main(){
int t,m;
scanf("%lld%lld",&t,&m);
for(int i = 1;i <= m;i++){
scanf("%lld%lld",&c[i],&v[i]);
}
for(int i = 1;i <= m;i++){
for(int j = c[i];j <= t;j++){
dp[j] = max(dp[j - c[i]] + v[i],dp[j]);
}
}
printf("%lld",dp[t]);
return 0;
}
3.多重背包:介于1,2之间
思路:进行合成 转化为01背包
合成方法:最好是2进制优化(O(N)->O(logN))
//此处未完待续
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10, M = 2e6 + 100;
int n,m,T,ans = 0,sum = 0;
int v[N],w[N],f[N];
void solve(){
cin >> n >> m;
int cnt = 1;
for(int i = 1 ;i <= n ; i ++){
int a,b,c; //体积,重量,数量
cin >> a >> b >> c;
int k = 1;//(2的0次等于1)
//将53---->转换成22的过程
while(k<=c){
v[cnt] = a * k;
w[cnt++] = b * k;
c-=k;
k*=2;//2的k次
}
//22也放入01中
if(c>0){
v[cnt] = a * c;
w[cnt++] = b * c;
}
}
for(int i = 1 ; i <= cnt ; i ++){
for(int j = m ; j >= v[i] ; j --){
f[j] = max(f[j],f[j-v[i]] + w[i]);
}
}
cout << f[m] << endl;
}
signed main(){
solve();
return 0;
}