背包问题

一.01背包

有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。
f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。

状态转移方程:f[i][v]=max{ f[i-1][v] , f[i-1][v-c[i]]+w[i] }

将前i件物品放入容量为v的背包中”这个子问题,若只考虑第i件物品的策略(放或不放),那么就可以转化为一个只牵扯前i-1件物品的问题。如果不放第i件物品,那么问题就转化为“前i-1件物品放入容量为j的背包中”,价值为f[i-1][v];如果放第i件物品,那么问题就转化为“前i-1件物品放入剩下的容量为v-c[i]的背包中”,此时能获得的最大价值就是f[i-1][v-c[i]]再加上通过放入第i件物品获得的价值w[i]。

 
#include<bits/stdc++.h>
 
using namespace std;
 
int dp[1005][1005];
 
int weight[1005];
 
int value[1005];
 
int main()
 
{
 
    int n,m;
 
    cin>>m>>n;
 
    memset(dp,0,sizeof(dp));//数组清空,其实同时就把边界给做了清理
 
    for(int i=1; i<=n; i++)
 
        cin>>weight[i]>>value[i];
 
    //从1开始有讲究的因为涉及到dp[i-1][j],从0开始会越界
 
    for(int i=1; i<=n; i++)//判断每个物品能否放进
 
    {
 
        for(int j=0; j<=m; j++)//对每个状态进行判断
 
        //这边两重for都可以倒着写,只是需要处理最边界的情况,滚动数组不一样
 
        {
 
            if(j>=weight[i])//能放进
 
                dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
 
 
 
            else dp[i][j]=dp[i-1][j];//不能放进
 
        }
 
    }
 
    cout<<dp[n][m]<<endl;
 
    return 0;
 
}

空间优化

 
#include<bits/stdc++.h>
 
using namespace std;
 
int dp[1005];//滚动数组的写法,省下空间省不去时间
 
int weight[1005];
 
int value[1005];
 
int main()
 
{
 
    int n,m;
 
    cin>>m>>n;
 
    memset(dp,0,sizeof(dp));
 
    for(int i=1; i<=n; i++)
 
        cin>>weight[i]>>value[i];
 
    for(int i=1; i<=n; i++)//对每个数判断,可反
 
    {
 
        for(int j=m; j>=weight[i]; j--)//这里这个循环定死,不能反,反了就是完全背包
 
        {
 
            dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);//其实不断在判断最优解,一层一层的
 
        }
 
    }
 
    cout<<dp[m]<<endl;
 
    return 0;
 
}

二.完全背包

特点:每种物品有无限件

 
#include<bits/stdc++.h>
 
using namespace std;
 
int dp[100005];//m
 
struct Node{
 
    int a,b;
 
}node[1005];//n
 
 
 
int main(){
 
    int n;
 
    while(~scanf("%d",&n)){
 
        for(int i=0;i<n;i++){
 
            scanf("%d%d",&node[i].a,&node[i].b);
 
        }
 
        int m;
 
        scanf("%d",&m);
 
        memset(dp,0,sizeof(dp));
 
        for(int i=0;i<n;i++){
 
            for(int j=node[i].b;j<=m;j++){//这样就是完全背包
 
                dp[j]=max(dp[j],dp[j-node[i].b]+node[i].a);
 
            }
 
        }
 
        printf("%d\n",dp[m]);
 
    }
 
    return 0;
 
}

三.多重背包

特点:每种物品有有限件num[i]

可以把物品拆开,把相同的num[i]件物品 看成 价值跟重量相同的num[i]件不同的物品

 
#include<bits/stdc++.h>
 
using namespace std;
 
int dp[1005];
 
int weight[1005],value[1005],num[1005];
 
int main()
 
{
 
    int n,m;
 
    cin>>n>>m;
 
    memset(dp,0,sizeof(dp));
 
    for(int i=1; i<=n; i++)
 
        cin>>weight[i]>>value[i]>>num[i];
 
        
 
    for(int i=1; i<=n; i++)//每种物品
 
        
 
        for(int k=0; k<num[i]; k++)//其实就是把这类物品展开,调用num[i]次01背包代码
 
        
 
            for(int j=m; j>=weight[i]; j--)//正常的01背包代码
 
            
 
                dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);
 
    
 
    cout<<dp[m]<<endl;
 
    return 0;
 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值