01背包
-
状态转移方程(w[i]重量,v[i]值)
f[i][j] = max{ f[i-1][j], f[i-1][j-w[i]]+v[i] } (j-w[i]>=0)
f[i][j] = f[i-1][j]; (j-wl[i]< 0)
HDU - 2602 (模板题)
Description
一个人在收集骨头,他有一个容积为V 的背包,不同的骨头体积和价值都有可能不同,现在他想知道他用这个背包最多可以带走多少价值的骨头。
Input
输入数据的第一行是一个正整数,表示一共有几组数据。
每组测试数据首先输入两个整数 n, V (n<=1000, V <=1000) 表示骨头的个数,和背包的容积,
接下来输入两行:
第一行有 n 个整数表示每个骨头的价值,•第二行有n 个整数表示每个骨头的体积;
1.用二维数组实现
时O(n*W),空O(n*W)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e3+5;
int n,w[N],v[N];
int dp[N][N],W;
int main(){
int t;
scanf("%d",&t);
while(t--){
memset(dp,0,sizeof(dp));
memset(w,0,sizeof(w));
memset(v,0,sizeof(v));
scanf("%d%d",&n,&W);
for(int i=0;i<n;i++){
scanf("%d",&v[i]);
}
for(int i=0;i<n;i++) scanf("%d",&w[i]);//这是体积哦
///初始化,由于涉及到(i-1),需要单独列出来,也就是第一次放入物品
for(int i=w[0];i<=W;i++) dp[0][i]=v[0];
for(int i=1;i<n;i++){///将前i件物品放入到容量为j的背包中
for(int j=0;j<=W;j++){
if(j<w[i]){///不选
dp[i][j]=dp[i-1][j];
}
else{///选
dp[i][j]=max(dp[i-1][j]+v[i-1],dp[i-1][j-w[i]]+v[i]);
}
}
}
for(int i=0;i<n;i++){
for(int j=0;j<=W;j++){
printf("%d ",dp[i][j]);
}
printf("\n");
}
printf("%d\n",dp[n-1][W]);
}
return 0;
}
输入:
1 //数据组数
5 10 //骨头的个数,背包的容积
1 2 3 4 5 //骨头的价值
5 4 3 2 1 //骨头所占的体积
输出
0 0 0 0 0 1 1 1 1 1 1///第一根(1.被放入:大于空间为5的背包,其价值为1;2.未被放入,价值为0)
0 0 0 0 2 2 2 2 2 3 3///第二根(1.被放入:大于空间4的背包(第一根((1).被放入:大于空间为5的背包(j>=9),其价值为3;
///(2).未被放入(j>=4&&j<9),价值为2))。2.未被放入:第一根(1.被放入:大于空间为5的背包(j>=5&&j<9),(与红色部分相比,取最大值)其价值为2;2.未被放入,(j<4)价值为0));下面的按照这个方式
0 0 0 3 4 4 4 5 5 5 5
0 0 4 6 7 7 8 8 8 9 9
0 5 8 10 11 12 12 13 13 13 14
14
2.用一位数组实现
时O(n*W),空O(1)
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+5;
int n,W,w[N],v[N];
int f[N];
int main(){
int t;
scanf("%d",&t);
while(t--){
memset(f,0,sizeof(f));
memset(w,0,sizeof(w));
memset(v,0,sizeof(v));
scanf("%d%d",&n,&W);
for(int i=0;i<n;i++) scanf("%d",&v[i]);
for(int i=0;i<n;i++) scanf("%d",&w[i]);
for(int i=0;i<n;i++){///一根一根装骨头
for(int j=W;j>=w[i];j--){///(1.装:空间能装下下一根骨头(肯定是这个大啦)2.不装;)取最大值
f[j]=max(f[j],f[j-w[i]]+v[i]);
printf("%d ",f[j]);
}
printf("\n");
}
printf("-------------\n");
for(int i=0;i<=W;i++)
printf("%d ",f[i]);
printf("\n");
}
return 0;
}
/*
INPUT:
1
5 10
1 2 3 4 5
5 4 3 2 1
OUTPUT:
1 1 1 1 1 1///W>=5时,能装第一根骨头,
3 3 2 2 2 2 2
5 5 5 5 3 3 3 3
9 9 7 7 7 7 4 4 4
14 12 12 12 12 9 9 9 5 5
-------------
0 5 5 9 9 9 12 12 12 12 14
Process returned 0 (0x0) execution time : 3.432 s
Press any key to continue.
*/
思路:为了使饭卡的钱最低,最后一次买的一定是最贵的菜(且保证选最后一次前,饭卡中的钱>=5)
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+5;
int n,m,d[N],f[N];
int main(){
while((scanf("%d",&n)!=EOF)&&n){///n个菜
memset(d,0,sizeof(d));
memset(f,0,sizeof(f));
for(int i=1;i<=n;i++) scanf("%d",&d[i]);
scanf("%d",&m);
sort(d+1,d+n+1);///按菜价从小到大排序,最后一个很特殊,
if(m<5) printf("%d\n",m);
else{
for(int i=1;i<n;i++){///d[n]最大
for(int j=m-5;j>=d[i];j--){///j>=d[i]决定能否装下一个
f[j]=max(f[j],f[j-d[i]]+d[i]);
printf("%d ",f[j]);
}
printf("\n");
}
printf("%d\n",m-f[m-5]-d[n]);//f[m-5]除最后一次的总消费
}
}
return 0;
}
完全背包
状态转移方程(对比01背包)
f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v} (k为每种物品数量)
HRBUST - 1053 (模板题)
1.二维数组解法
时间复杂度
O(V*Σ(V/c[i]))
///此方式超出内存,但可以理解下
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e3+5;
int f[N][N],w[N],v[N];
int G,U,T;
int main(){
scanf("%d",&T);
while(T--){
memset(f,0,sizeof(f));
memset(w,0,sizeof(w));
memset(v,0,sizeof(v));
scanf("%d%d",&G,&U);
for(int i=0;i<U;i++)
scanf("%d%d",&v[i],&w[i]);
for(int i=0;i<U;i++){///装i中物品
for(int j=0;j<=G;j++){///
for(int k=0;k*w[i]<=j;k++){///背包空间为j时,第i种物品能放k个
f[i+1][j]=max(f[i+1][j],f[i][j-k*w[i]]+k*v[i]);
}
printf("%d ",f[i+1][j]);
}
printf("\n");
}
printf("%d\n",f[U][G]);
}
return 0;
}
/*
输入
1
10 5
1 5
2 4
3 3
4 2
5 1
输出
0 0 0 0 0 1 1 1 1 1 2///背包空间为j时(j-5>=0),第1种物品能放几个的值之和
0 0 0 0 2 2 2 2 4 4 4//j-4>=0时,可用的背包值之和,同01背包
0 0 0 3 3 3 6 6 6 9 9
0 0 4 4 8 8 12 12 16 16 20
0 5 10 15 20 25 30 35 40 45 50
50
*/
2.一维数组解法(对比01背包)
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int N=1e4+5;
int T,G,U;
int v[N],c[N];
int f[N];
int main(){
scanf("%d",&T);
while(T--){
scanf("%d%d",&G,&U);
memset(f,0,sizeof(f));
memset(v,0,sizeof(v));
memset(c,0,sizeof(c));
for(int i=1;i<=U;i++) scanf("%d%d",&v[i],&c[i]);
for(int i=1;i<=U;i++){
for(int j=c[i];j<=G;j++){///每种unit可以选多个,顺序(和01背包比较)
f[j]=max(f[j],f[j-c[i]]+v[i]);
}
}
printf("%d\n",f[G]);
}
return 0;
}
3.多重背包
状态转移方程
HDU - 2191 (模板题)
//好好理解前两种,这个就能写出
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e3+5;
int C,n,m,p[N],h[N],c[N];
int f[N][N];
int main(){
scanf("%d",&C);
while(C--){
memset(p,0,sizeof(p));
memset(h,0,sizeof(h));
memset(c,0,sizeof(c));
memset(f,0,sizeof(f));
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++){
scanf("%d%d%d",&p[i],&h[i],&c[i]);
}
for(int i=0;i<m;i++){
for(int j=0;j<=n;j++){
for(int k=0;k*p[i]<=j&&k<=c[i];k++){
if(j<p[i]){
f[i+1][j]=f[i][j];
}
else {
f[i+1][j]=max(f[i+1][j],f[i][j-k*p[i]]+k*h[i]);
}
}
}
}
printf("%d\n",f[m][n]);
}
return 0;
}
欢迎指点,不喜勿喷