笔试题型整理1:背包问题

1. 0-1背包问题

  • 一件物品只有选与不选两种情况,也就是说每件物品只能被选一次。

[编程题]01背包

f[ i ][ j ] 表示只看前 i 个物品,总体积是 j 的情况下,总价值最大是多少

result = max{f[ n ][ 0~v ]}

转移方程:
f[ i ][ j ]:

  1. 不选第 i 个物品, f[ i ][ j ] = f[ i - 1 ][ j ];
  2. 选第 i 个物品, f[ i ][ j ] = f[ i - 1 ][ j - v[ i ]] + w[ i ];

f[ i ][ j ] = max{1. 2.}

在这里插入图片描述
在这里插入图片描述

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 1010;

int n,m;
int f[N];
int v[N], w[N];

int main(){
    cin >> n >> m;
    for(int i=1;i <= n;++i) cin >> v[i] >> w[i];
    
    for(int i=1;i<=n;++i){ // 先循环物品
        for(int j=m;j>=v[i];--j){ // 再循环体积
            f[j] = max(f[j],f[j-v[i]]+w[i]);
        }
    }
    cout << f[m] << endl;
    return 0;
}

2. 完全背包问题

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 1010;

int n,m;
int f[N];

int main(){
    cin >> n >> m;
    for(int i=0;i<n;i++){
        int v,w;
        cin >> v >> w;
        for(int j = v; j <= m;j++){
            f[j] = max(f[j], f[j - v] + w);
        }
    }
    
    cout << f[m] << endl;
    return 0;
}

注意:

在状态转移方程处:
01背包是从大到小枚举,完全背包是从下到大枚举。

3. 多重背包问题

  • 每件物品有选择次数限制
  • 可以看作01背包问题的扩展,枚举时也是从大到小

多重背包问题

在这里插入图片描述
在这里插入图片描述

/*
f[i] 总体积是i的情况下,最大价值是多少;

for(int i = 0;i < n; i++){
    for(int j = 0;j >= v[i];j--){
        f[j] = max(f[j],f[j - v[i]] + w[i], f[j - 2 * v[i]] + 2 * w[i]...); // 可以选0个,1个,2个等等  有x+1种选法
    }
}

1. f[i] = 0;
f[m]

2. f[0] = 0;f[i] = -INF, i != 0;
*/

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 110;

int n,m;
int f[N];

int main(){
    cin >> n >> m;
    for(int i=0;i < n;i++){
        int v,w,s;
        cin >> v >> w >> s;
        for(int j=m;j>=0;j--){
            for(int k=1;k<=s && k*v <= j;k++){
                f[j] = max(f[j], f[j - k*v] + k*w);
            }
        }
    }
    cout << f[m] << endl;
    return 0;
}

当数据范围很大时,需要使用到二进制优化方法来解决多重背包问题

多重背包问题 II

在这里插入图片描述
在这里插入图片描述

/*
v,m

log(s)
把s拆成 log(s) 份;

时间复杂度就会降成 1000 * 11 * 2000 = 2 * 10^7 其中11是log(2000)的近似

*/

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

const int N = 2010;

int n,m;
int f[N];

struct Good{
    int v , w;
};

int main(){
    vector<Good> goods;
    cin >> n >> m;
    for(int i=0;i<n;++i){
        int v, w, s;
        cin >> v >> w >> s;
        for(int k=1 ; k<=s ; k*=2){
            s -=k;
            goods.push_back({v*k,w*k});
        }
        if(s > 0) goods.push_back({v*s, w*s});
    }
    for(auto good:goods){
        for(int j=m ; j >= good.v;j--){
            f[j] = max(f[j], f[j-good.v]+good.w);
        }
    }
    cout << f[m] << endl;
    return 0;
}

当数据范围更大时,需要使用到多重背包的单调队列优化方法来解决

在这里插入图片描述
在这里插入图片描述

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

const int N = 20010;

int n,m;
int f[N], g[N], q[N];

int main(){
    cin >> n >> m;
    for(int i=0;i<n;i++){
        int c, w, s;
        cin >> c >> w >> s;
        memcpy(g,f,sizeof f);
        
        for(int j=0;j<c;j++){
            int hh =0 ,tt =-1;
            for(int k=j;k<=m;k+=c){
                f[k] = g[k];
                if(hh <= tt && k-s*c > q[hh]) hh++;
                if(hh <= tt) f[k] = max(f[k],g[q[hh]] + (k - q[hh])/c*w);
                while(hh <= tt && g[q[tt]] - (q[tt] - j) / c*w <= g[k] - (k-j)/c*w) tt--;
                q[++tt] = k;
            }
        }
    }
    cout << f[m] << endl;
    
    return 0;
}

4. 混合背包问题

  • 有不同种类的物品,以及不同的限制,问背包中最大价值为多少
    在这里插入图片描述
    在这里插入图片描述
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

const int N = 1010;

int n,m;
int f[N];

struct Thing{
    int kind;
    int v,w;
};
vector<Thing> things;

int main(){
    cin >> n >> m;
    for(int i=0;i<n;++i){
        int v,w,s;
        cin >> v >> w >> s;
        if(s < 0) things.push_back({-1,v,w});
        else if(s == 0) things.push_back({0,v,w});
        else{
            for(int k=1;k<=s;k*=2){
                s-=k;
                things.push_back({-1,v*k,w*k});
            }
        if(s>0) things.push_back({-1,v*s,w*s});
        }
    }
    for(auto thing:things){
        if(thing.kind < 0){ // 如果是01背包问题从大到小枚举
            for(int j=m;j >= thing.v;j--) f[j] = max(f[j],f[j - thing.v] + thing.w);
        }else{// 如果是完全背包问题从小 到大枚举
            for(int j=thing.v;j <= m;j++) f[j] = max(f[j],f[j - thing.v] + thing.w);
        }
    }
    
    cout << f[m] << endl;
    return 0;
}

5. 二维费用的背包问题

在这里插入图片描述
在这里插入图片描述

/*
    体积是i 重量是j 时价值的最大值
    f[i][j]
*/
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 110;

int n, v, m;
int f[N][N];

int main(){
    cin >> n >> v >> m;
    for(int i=0;i < n;++i){
        int a,b,c;
        cin >> a >> b >> c;
        for(int j = v; j >=a ;j--){
            for(int k = m; k>=b;k--){
                f[j][k] = max(f[j][k] , f[j - a][k - b]+c);
            }
        }
    }
    cout << f[v][m] << endl;
    return 0;
}

6. 分组背包问题

  • 先把各种个样的物品分为若干组,然后每组里面只能选一件

在这里插入图片描述

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 110;

int n,m;
int f[N], v[N], w[N];

int main(){
    cin >> n >> m;
    for(int i=0;i<n;i++){
        int s;
        cin >> s;
        for(int j=0;j<s;j++){
            cin >> v[j] >> w[j];
        }
        for(int j=m;j>=0;j--){
            for(int k=0;k<s;++k){
                if(j>=v[k])
                f[j] = max(f[j],f[j - v[k]] + w[k]);
            }
        }
    }
    cout << f[m] << endl;
    return 0;
}

7. 背包问题求方案数

8. 求背包问题的方案

在这里插入图片描述
在这里插入图片描述

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1010;

int n,m;
int v[N],w[N],f[N][N];

int main(){
    cin >> n >> m;
    for(int i=1;i<=n;i++) cin >> v[i] >> w[i];
    
    for(int i=n;i>=1;i--){
        for(int j=0;j<=m;j++){
            f[i][j] = f[i+1][j];
            if(j>=v[i]) f[i][j] = max(f[i][j],f[i+1][j-v[i]]+w[i]);
        }
    }
    
    int tmp=m;
    for(int i=1;i<=n;i++){
        if(tmp-v[i]>=0&&f[i][tmp]==f[i+1][tmp-v[i]]+w[i]) {//tmp-v[i]>=0,只有当前物品能放进去,才比较放与不放的价值大小,如果放不进去,直接下一个物品
            cout<<i<<' ';
            tmp-=v[i];
        }
    }
    return 0;
}

9. 有依赖的背包问题

  • 比如说有某种东西必须选

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;

const int N = 110;

int n,m;
int h[N], e[N], ne[N], idx; // h是临接表的头,e是edge,ne是next edge
int v[N], w[N], f[N][N];

void add(int a, int b){
    e[idx] = b, ne[idx] = h[a], h[a] =idx++;
}

void dfs(int u){
    for(int i=h[u];i != -1;i = ne[i]){
        int son = e[i];
        dfs(son);
        for(int j=m-v[u];j>=0;j--)
            for(int k=0;k<=j;k++)
            f[u][j] = max(f[u][j],f[u][j-k]+f[son][k]);
    }
    for(int i=m;i >= v[u];i--) f[u][i] = f[u][i - v[u]] + w[u];
    for(int i=0;i < v[u];i++) f[u][i] = 0;
}

int main(){
    memset(h,-1,sizeof h);
    cin >> n >> m;
    int root;
    for(int i=1;i<=n;++i){
        int p;
        cin >> v[i] >> w[i] >> p;
        if(p == -1) root = i;
        else add(p,i);
    }
    dfs(root);
    cout << f[root][m] << endl;
    
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值