简单摘抄一下01背包问题以及解决该类问题的基本代码语句
问题简述:
有n件物品,每件物品的重量为w[i],价值为c[i]。现有一个容量为V的背包,问如何选取物品放入背包,使得背包内的物品总价值最大。其中每种物品都只有1种。
令dp[i][v]表示前i件物品(1 <= i <= n, 0 <= v <= V)恰好装入容量为v的背包中所获得的最大价值。
考虑对第i件物品的选择策略,
1.不放第 i 件物品,那么问题转化为前 i-1 件物品恰好装入容量为 v 的背包中所能获得的最大价值,也即dp[i-1][v];
2.放第 i 件物品,那么问题转化为前 i-1 件物品恰好装入容量为 v - w[i] 的背包中所能获得的最大价值,也即dp[i-1][v-w[i]+c[i]]。
实现代码:
for(int i = 1; i<=n; i++){
for (int v = w[i]; v<=V; v++){
dp[i][v]=max(dp[i-1][v],dp[i-1][v-w[i]]+c[i]);
}
}
计算dp[i][v]时总是只需要dp[i-1][v]左侧部分的数据,当计算dp[i+1][]的部分时,dp[i-1][]的数据完全用不到(只需要用到dp[i][]),因此不妨直接开一个一维数组dp[v],枚举方式改变为 i 从1到 n ,v 从 V 到 0(逆序)
由此改变实现代码如下:
for ( int i = 1; i <= n ; i++){
for (int v = V; v >= w[i] ; v--){
dp[v] = max ( dp[v], dp[v-w[i]]+c[i]);
}
}
实际问题:
用n个硬币买价值为m的东西,输出使用方案,使得正好几个硬币加起来价值为m。从小到大排列,输出最小的那个排列方案
1.排列方案从小到大,先将硬币面值从大到小排列
2.bool类型的 choice[i][j] 数组 dp[i][j] 是否选取,如果选取了就令 choice 为 true
#include <iostream>
#include <cmath>
#include <vector>
#include <algorithm>
#include <map>
using namespace std;
bool choice[10010][110];
int cmp(int a,int b){
return a>b;
}
int main(){
int n,m;
scanf("%d %d",&n,&m);
int a[n+1];
for (int i =1;i<=n;i++){
scanf("%d",&a[i]);
}
sort(a+1,a+n+1,cmp);
int dp[m];
for (int v=0;v<=m;v++){
dp[v]=0;
}
vector<int > vec;
for (int i=1;i <= n; i++){
for (int v = m; v >= a[i];v--){
if (dp[v]<=dp[v-a[i]]+a[i]){
dp[v]=dp[v-a[i]]+a[i];
choice[i][v]=true;
}
}
}
if (dp[m]!=m){
printf("No Solutio");
}
else {
vector <int > arr;
int v=m,index = n;
while (v>0){
if (choice[index][v]==true){
arr.push_back(a[index]);
v=v-a[index];
}
index--;
}
for (int i =0;i<arr.size() ;i++){
if (i!=0){
printf(" ");
}
printf("%d",arr[i]);
}
}
return 0;
}