动态规划
背包问题
背包问题的思考方向是(闫式分析法)
例如:
集合表示的含义:所有从前i个物品中选,而且总体积恰好是j的所有方案。
那么这个含义代表的属性是:数量
集合表示的含义是:所有从前i个物品中选,总体积不超过j的选法。
属性:max
01背包问题
性质:每一件物品只能选择一次。
注意:
1.01背包问题其实需要从后向前遍历,因为每一件物品只能选择一次,那么在这一次遍历的过程当中,前面的数据都没有被计算过,因此,就可以做到利用前面的数据进行求解(前面的数值没有被改变,从后往前遍历,本次遍历的改变是从后向前改变的)
2.01背包问题需要注意的是恰好存满的情况和不恰好存满的情况略有区别,需要注意这个问题!
acwing–>01背包习题1
这道题的大致思路就是,先让ans[0]=1,然后再下面遍历的过程中,每到达一个数,能都踩上一个点,方法数得以累加。
#include<iostream>
using namespace std;
int ans[21000202];
int main(){
int n,v,p;
cin>>n>>v;
ans[0]=1;
for(int i=1;i<=n;i++){
cin>>p;
for(int j=v;j>=p;j--) ans[j]+=ans[j-p];
}
cout<<ans[v]<<endl;
return 0;
}
acwing–>01背包习题2
这道题和标准01背包问题类似,把ans[n]=p数组看成n这个大小的背包里面存放价值p的东西,而本道题的价值是我们的背包里面存放的体积大小。
#include<iostream>
using namespace std;
int ans[210200];
//物品体积看做成物品价值求解
int main(){
int v,n;
cin>>v>>n;
for(int i=1;i<=n;i++){
int vv;
cin>>vv;
for(int j=v;j>=vv;j--){
ans[j]=max(ans[j],ans[j-vv]+vv);
}
}
cout<<v-ans[v]<<endl;
return 0;
}
acwing–>01背包习题3
如果说前面两个问题是可以不用恰好装满问题(只用初始化为0),那么这个问题就是恰好装满问题(需要初始化为负无穷)。
#include<iostream>
#include<cstring>
using namespace std;
int c[21021020];
int ans[2102][2102];
int main(){
int n,k;
cin>>n>>k;
memset(ans,-0x3f,sizeof ans);
ans[0][0]=0;
for(int i=1;i<=n;i++) cin>>c[i];
for(int i=1;i<=n;i++){
for(int j=0;j<k;j++){
ans[i][j]=max(ans[i-1][j],ans[i-1][(j+k-c[i]%k)%k]+c[i]);
}
}
cout<<ans[n][0]<<endl;
return 0;
}
完全背包问题
性质和区别:
01背包问题和完全背包问题的不一样之处在于01背包问题是每一件物品只选择一次,而完全背包问题是每一件物品可以选无数次。
注意:
和01背包问题不同之处,在于完全背包问题每一次遍历是从前往后依次遍历的,因为可以放无数次,所以前面的集合运算完成以后,后面的也可以接着运算,因为没有了选择物品的次数限制。
acwing完全背包习题01
这个题分析性质:每一个物品你拿走的次数没有限制,因此是一个完全背包,又因为是种类数,所以性质是数量,因此根据动态规划完全背包从前往后递推的性质,应该是每一次的种类数都是由变形前的数加起来,当然了a[0]=1需要有这一步的初始化。下面请看代码:
#include<iostream>
using namespace std;
typedef long long ll;
ll ans[21021020];
int main(){
ll n;
cin>>n;
ans[0]=1;
for(ll i=1;i<n;i++){
for(ll j=i;j<=n;j++) ans[j]+=ans[j-i],ans[j]%=2147483648;
//从前往后依次遍历
}
cout<<ans[n]<<endl;
return 0;
}
多重背包问题
性质:多重背包问题就是每一件物品都只能选择有限次数。
优化: