DP—背包问题

DP—背包问题

基本0-1背包问题

题设:有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大

f[i][j]=max( f[i-1][j] , f[i-1][j-w[i]]+v[i]) //f[i][j]表示前i件作品占容量j时的最大价值;注意这个占的容量并不一定占满
第i件物品是否要放入容量j的背包中取决于是原有最好的的物品放入价值高还是在加入这个物品价值高;
常规解法:
#include<iostream>
#include<stdio.h>
#include<string.h>
const int len_A=1e5;
using namespace std;
int find_A(int m,int n);
int ans_A[len_A];
int w_A[len_A],c_A[len_A];
int main(){
    int n,v;
    while(cin>>n>>v){
        for(int i=1;i<=n;i++){
            cin>>c_A[i]>>w_A[i];
        }
        cout<<find_A(n,v)<<endl;
    }
    return 0;
}
int find_A(int n,int v){
    memset(ans_A,0,sizeof ans_A);
    for(int i=1;i<=n;i++){
        for(int j=v;j>0;j--){
            if(c_A[i]<=j)
                ans_A[j]=max(ans_A[j],ans_A[j-c_A[i]]+w_A[i]);
        }
    }
    return ans_A[v];
}
解二:
#include <iostream>
#include<stdio.h>
using namespace std;
#define MAX(m,n) (((m)>(n))?(m):(n))
int n,v;
int V[501][30001];//设置背包时其实可以不要i值,因为每次循环的i值都是固定的
void value(int goods[][2]);
int main(){
    while((scanf("%d %d",&n,&v))!=EOF){
    int goods[n][2];//{weight,value}
    for(int i=0;i<n;i++){
        scanf("%d %d",&goods[i][0],&goods[i][1]);
    }
    value(goods);
    int result=V[n][v];
    printf("%d\n",result);
	}
    return 0;
}
void value(int goods[][2]){
  for(int i=0;i<=n;i++){
    V[0][i]=0;//若要求恰好装满背包,应该在初始化时除了f[0][0]之外所有f[0][i]都设为负无穷;
  }
  for(int i=1;i<=n;i++){
      for(int w=0;w<=v;w++){
          if(w<goods[i-1][0]){
              V[i][w]=V[i-1][w];
          }
          else{
              V[i][w]=MAX(V[i-1][w],V[i-1][w-goods[i-1][0]]+goods[i-1][1]);
          }
      }
  }
}
完全背包问题

题设:每件物品都数量无限

加一个循环每件物品
#include<iostream>
#include<stdio.h>
#include<string.h>
const int len_B=1e5;
using namespace std;
int find_B(int m,int n);
int ans_B[len_B];
int w_B[len_B],c_B[len_B];
int main(){
    int n,v;
    while(cin>>n>>v){
        for(int i=1;i<=n;i++){
            cin>>c_B[i]>>w_B[i];//费用与价值
        }

        cout<<find_B(n,v)<<endl;
    }
    return 0;
}
int find_B(int n,int v){
    memset(ans_B,0,sizeof ans_B);
    for(int i=1;i<n;i++){//一个优化排序过程,每件物品与其后物品作比较看价值高质量小者可以直接替换掉比它差者
        for(int j=i+1;j<=n;j++){
            if(w_B[i]<=w_B[j]&&c_B[i]>=c_B[j]){
                w_B[i]=w_B[n],c_B[i]=c_B[n]; //被替换者放到最后去掉
                n--,i--;
                break;
            }
            else if(w_B[i]>=w_B[j]&&c_B[i]<=c_B[j]){
                w_B[j]=w_B[n],c_B[j]=c_B[n];
                n--,j--;
            }
        }
    }
    for(int i=1;i<=n;i++){ //选择物品的过程,空间优化只用了一位数组,第i件物品选到容量满
        for(int j=0;j<=v;j++){ //这里为什么把初始值j从v改成0就可以了?当从前往后时,第i件物品想用几次就用几次
            if(c_B[i]<=j)
                ans_B[j]=max(ans_B[j],ans_B[j-c_B[i]]+w_B[i]);
        }
    }
    return ans_B[v];
}
多重背包问题

题设:第i种物品最多有m[i]件可用

#include<iostream>
#include<stdio.h>
#include<string.h>
const int len_C=1e5;
using namespace std;
int find_C(int m,int v);
void Completepack_C(int c,int w,int v);
void ZeroOnepack_C(int c,int w,int v);
void Multiplepack_C(int c,int w,int amount,int v);
int ans_C[len_C];
int w_C[len_C],c_C[len_C],m[len_C];
int main(){
    int n,v;
    while(cin>>n>>v){
        for(int i=1;i<=n;i++){
            cin>>c_C[i]>>w_C[i]>>m[i];
        }
        cout<<find_C(n,v)<<endl;
    }
    return 0;
}
int find_C(int n,int v){
    memset(ans_C,0,sizeof ans_C);
    for(int i=1;i<=n;i++){
        Multiplepack_C(c_C[i],w_C[i],m[i],v);
    }
    return ans_C[v];
}
void Multiplepack_C(int c,int w,int amount,int v){
    if(c*amount>=v){//当i的数量大于背包容量时相当于一个完全背包
        Completepack_C(c,w,v);
        return;
    }
    for(int k=1;k<amount;){//当i的数量有限时,用01a背包法k个k个放
        ZeroOnepack_C(k*c,k*w,v);
        amount-=k;
        k=k<<1;//k乘2;为什么这么设计?节省时间,1,2,4...2^k−1,可以组成小于amount的所有搭配,在一次次01背包中不断选择最优搭配;
    }
    ZeroOnepack_C(amount*c,amount*w,v);
}
void ZeroOnepack_C(int c,int w,int v){//01背包,
    for(int j=v;j>0;j--){
        if(c<=j)
            ans_C[j]=max(ans_C[j],ans_C[j-c]+w);
    }
}
void Completepack_C(int c,int w,int v){//完全背包
    for(int j=0;j<=v;j++){
        if(c<=j)
            ans_C[j]=max(ans_C[j],ans_C[j-c]+w);
    }
}
组合背包问题

题设:01背包与完全背包与多重背包的混合(和多重背包差不多)

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
const int len_D=1e5;
int find_D(int m,int v);
void Completepack_D(int c,int w,int v);
void ZeroOnepack_D(int c,int w,int v);
void Multiplepack_D(int c,int w,int amount,int v);
int ans_D[len_D];
int w_D[len_D],c_D[len_D],m[len_D];
int main(){
    int n,v;
    while(cin>>n>>v){
        for(int i=1;i<=n;i++){
            cin>>c_D[i]>>w_D[i]>>m[i];
        }
        cout<<find_D(n,v)<<endl;
    }
    return 0;
}
int find_D(int n,int v){
    memset(ans_D,0,sizeof ans_D);
    for(int i=1;i<=n;i++){
        if(m[i]==233)
            Completepack_D(c_D[i],w_D[i],v);/
        else
            Multiplepack_D(c_D[i],w_D[i],m[i],v);
    }
    return ans_D[v];
}
void Multiplepack_D(int c,int w,int amount,int v){
    if(c*amount>=v){
        Completepack_D(c,w,v);
        return;
    }
    for(int k=1;k<amount;){
        ZeroOnepack_D(k*c,k*w,v);
        amount-=k;
        k=k<<1;
    }
    ZeroOnepack_D(amount*c,amount*w,v);
}
void ZeroOnepack_D(int c,int w,int v){
    for(int j=v;j>0;j--){
        if(c<=j)
            ans_D[j]=max(ans_D[j],ans_D[j-c]+w);
    }
}
void Completepack_D(int c,int w,int v){
    for(int j=0;j<=v;j++){
        if(c<=j)
            ans_D[j]=max(ans_D[j],ans_D[j-c]+w);
    }
}
二维背包问题

题设:选择每件物品必须同时付出两种代价;对于每种代价都有一个可付出的最大值(背包容量)第i件物品所需的两种代价分别为w[i]和g[i] 两种代价可付出的最大值(两种背包容量)分别为V和T。物品的价值为v[i]。
这种背包有多种形式,总的来说就是再加一个条件,多一个维度;

核心:f[i][j][k]=max(f[i−1][j][k],f[i−1][j−w[i]][k−g[i]]+v[i]);
for (int i = 1; i <= n; i++)
	for (int j = V; j >= w[i]; j--)
    		for (int k = T; k >= g[i]; k--)
        		f[j][k] = max(f[j][k], f[j - w[i]][k - g[i]] + v[i]);
分组背包问题

题设:物品被划分为若干组,每组中的物品互相冲突,最多选一件

核心:f[k][j]=max(f[k−1][j],f[k−1][j−c[i]]+w[i]) //k表示组,i省去
	for (所有的组k)
		 for (int j = V; j >= 0; j--)
   			for (所有属于组k的i)
       				 f[j] = max{f[j], f[j - w[i]] + v[i]}

链接:https://blog.csdn.net/yandaoqiusheng/article/details/84782655

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值