01型背包问题解题总结(一维)

前言:01型背包问题(二维)的链接:

https://blog.csdn.net/qq_56430444/article/details/118160876

完全背包问题链接:

https://blog.csdn.net/qq_56430444/article/details/118389012

01型背包问题的模板(一维):

  1.状态:dp[i]表示为背包容量为i所能装下的最大价值

  2.状态转移方程: if( i>=w[j] ){ dp[i] = max ( dp[i-w[j]]+v[j] , dp[i] )

  3.循环方向:   for(枚举物品的编号,正向)

                          for(枚举背包的容量,逆向)   注意:这里是一般情况,个别情况请单独分析

                             决策(状态转移方程)

  4.初始化:看题目而定

01型背包问题经典例题(一维):

  1. (模板题)采药:最纯正的01型背包问题

  2.小A点菜(求恰好装满背包容量的方案数目的01型背包问题):

#include<bits/stdc++.h>
using namespace std;
main(){
    int n,m;
    cin>>n>>m;
    int food[n+1]={0};  
    int f[m+1]={0};
    for(int i=1;i<=n;i++){
        cin>>food[i];
    }
    f[0]=1;
    for(int i=1;i<=n;i++){
        for(int j=m;j>=1;j--){
            if(j>=food[i]){             // 这个大于”等于“号很关键
                f[j]+=f[j-food[i]];
            }
        }
    }
    cout<<f[m];
}

  3.五倍经验日:      关键点在于状态转移方程,赢了加经验,输了也加经验

#include<bits/stdc++.h>
using namespace std;
 typedef long long ll;
 ll m,n;
 ll w[1005],l[1005],u[1005],dp[1005];
 int main(){
     cin>>n>>m;
     for(int i=1;i<=n;i++){
        cin>>l[i]>>w[i]>>u[i];
     }
     for(int i=1;i<=n;i++){
         for(int j=m;j>=0;j--){
             if(j>=u[i]){
                 dp[j]=max(dp[j-u[i]]+w[i],dp[j]+l[i]);
             }else{
                 dp[j]+=l[i];
             }
        }
    }
    cout<<dp[m]*5<<endl;
 }

  4.最大约数和:        先要打表进行初始化,不然会超时

#include<bits/stdc++.h>
using namespace std;
int w[1005],n;
int dp[1005][1005];
int p(int x){
    int sum=0;
    for(int i=1;i<x;i++){
        if(x%i==0){
            sum+=i;
        }
    }
    return sum;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        w[i]=p(i);
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(i>=j){
                dp[i][j]=max(dp[i-j][j-1]+w[j],dp[i][j-1]);
            }else{
                dp[i][j]=dp[i][j-1];
            }
        }
    }
    cout<<dp[n][n]<<endl;
}

  5.考前临时抱佛脚(这题难点是将题目理解并且转到01型背包问题):

       思路:因为左右脑可以同时思考,要想时间最少,则必然左右脑的时间要趋于相同,故可以设另一个大脑的最大容量为T/2,这样就可以转化到了01背包问题。

#include<bits/stdc++.h>
using namespace std;
int num[10];
int sum=0;
int main(){
    for(int i=1;i<=4;i++){
        cin>>num[i];
    }
    for(int i=1;i<=4;i++){
        int al[34]={0};
        int summ=0;
        for(int j=1;j<=num[i];j++){
            cin>>al[j];
            summ+=al[j];
        }
        int dp[1300][34];
        for(int k=1;k<=summ/2;k++){
            for(int j=1;j<=num[i];j++){
                if(k>=al[j]){
                    dp[k][j]=max(dp[k-al[j]][j-1]+al[j],dp[k][j-1]);
                }else{
                    dp[k][j]=dp[k][j-1];
                }
            }
        }
        sum+=summ-dp[summ/2][num[i]];  //假设我们求的是右脑(当然左脑也行,这是对称的)最大
    }                                  //时间,这个最大时间与dp数组的状态的下标含义一样,故
    cout<<sum<<endl;                   //最大时间不超过T/2,则用总数减去右脑的最大时间数就是
}                                      //答案

  6.Subset sums 集合(求装满容量为j的背包的最大方案数的01型背包问题)

      此题的转换成01背包的思路与考前零时抱佛脚的思路相同,这里就不赘述了

#include<bits/stdc++.h>
using namespace std;
int dp[1600][40];
int w[40];
int n,sum=0;
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        w[i]=i;
        sum+=i;
    }
    if(sum%2!=0){
        cout<<0<<endl;
    }else{
        dp[0][0]=1;
        for(int i=1;i<=sum/2;i++){
            for(int j=1;j<=n;j++){
                dp[i][j]+=dp[i][j-1];
                if(i>=w[j]){
                    dp[i][j]+=dp[i-w[j]][j-1];
                }
            }
        }
        cout<<dp[sum/2][n]<<endl;
    }
}

  7.yyy2015co1u盘:(此题目将01背包和二分搜索相互结合起来)

  思路:刚读题时便形成一个朴素的思路,就是将接口大小从大到小枚举,再套01型背包问题,但结合数据范围我们可以想到这样必然超时,我们可以这样想随着接口增大,则当超过某个最小数值后,一定能满足题目要求,就像这样  0000.....0111111  (0表示不满足要求,1表示满足要求)   不难想到可以利用二分搜索查找这个区间的最小值,这样就将原有的o(n)降到o(logn)

#include<bits/stdc++.h>
using namespace std;
int n,p,s;
int w[1200],v[1200];
int dp[1200][1200];
int minn=9999999,maxn=0;
int m(int x){
    for(int i=1;i<=s;i++){
        for(int j=1;j<=n;j++){
            dp[i][j]=0;
        }
    }
    for(int i=1;i<=s;i++){
        for(int j=1;j<=n;j++){
            if(x>=w[j]&&i>=w[j]){
                dp[i][j]=max(dp[i-w[j]][j-1]+v[j],dp[i][j-1]);
            }else{
                dp[i][j]=dp[i][j-1];
            }
        }
    }
    return dp[s][n];
}
int main(){
    cin>>n>>p>>s;
    for(int i=1;i<=n;i++){
        cin>>w[i]>>v[i];
        if(w[i]>maxn){
            maxn=w[i];
        }
        if(w[i]<minn){
            minn=w[i];
        }
    }
    int l=minn,r=maxn;
    while(l<r){
        int mid=l+(r-l)/2;
        if(m(mid)>=p){
            r=mid;
        }else{
            l=mid+1;
        }
    }
    if(m(l)>=p){
        cout<<l<<endl;
    }else{
        cout<<"No Solution!"<<endl;
    }
}

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
动态规划是一种解决多阶段决策过程最优化问题的数学方法。而01背包问题是动态规划中的一个经典问题,它的解题思路如下: 1.问题抽象化:将问题抽象成一个背包装物品的问题,背包有一定的容量,物品有一定的体积和价值。 2.建立模:用一个二维数组dp[i][j]表示前i个物品放入容量为j的背包中所能获得的最大价值。 3.寻找约束条件:对于每个物品,可以选择放入背包或不放入背包,因此需要判断当前物品是否放入背包中。 4.判断是否满足最优性原理:在每个阶段,都需要选择当前状态下的最优解,以保证最终得到的解是全局最优解。 5.找大问题与小问题的递推关系式:对于每个物品,可以选择放入背包或不放入背包,因此可以得到递推公式:dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]),其中w[i]表示第i个物品的体积,v[i]表示第i个物品的价值。 6.填表:按照递推公式填写二维数组dp。 7.寻找解组成:最终的解为dp[n][m],其中n表示物品的数量,m表示背包的容量。可以通过回溯得到具体的解组成。 下面是Python代码实现,其中w和v分别表示物品的体积和价值,m表示背包的容量: ```python def knapsack(w, v, m): n = len(w) dp = [[0] * (m+1) for _ in range(n+1)] for i in range(1, n+1): for j in range(1, m+1): if j < w[i-1]: dp[i][j] = dp[i-1][j] else: dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i-1]] + v[i-1]) res = dp[n][m] items = [] j = m for i in range(n, 0, -1): if res <= 0: break if res == dp[i-1][j]: continue else: items.append(i-1) res -= v[i-1] j -= w[i-1] return items[::-1] ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值