动态规划之背包问题详解

01背包

  1. 状态转移方程(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.


*/

2.HDU - 2546 - 饭卡

思路:为了使饭卡的钱最低,最后一次买的一定是最贵的菜(且保证选最后一次前,饭卡中的钱>=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;
}

欢迎指点,不喜勿喷

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值