DP-背包DP

背包问题感觉主要还是01背包和完全背包,其他都花里胡哨orz
※※※最根本的状态转移思路:f[i][j]=max(f[i−1][j],f[i−1][j−w[i]]+v[i])
※※※也就是说当我们从头到尾遍历每一个物品,对于每一个物品都有装or不装两种,在背包容量固定的情况下(这里的j,看成容量上限),是否选用这个物品,如果选用,转移到[j-w[i]]这个容量的时候,不选这个物品,就直接看下一个。
注意,不管选不选这个物品,容量上限都是不变的,我们可以依此进行状态转移。
资料:

背包九讲

经典背包九讲,这篇博客还带了自己的一些理解

背包六问

力扣上的背包六问,题型还是很经典的

模板

//01背包模板(+优化)for (int i = 1; i <= n; i++)
    for (int j = V; j >= w[i]; j--)//逆序
        f[j] = max(f[j], f[j - w[i]] + v[i]);

//完全背包模板(+优化)
for (int i = 1; i <= n; i++)
    for (int j = w[i]; j <= V; j++)//顺序
        f[j] = max(f[j], f[j - w[i]] + v[i]);
//多重背包,我觉得有点用到倍增的思想
for (int i = 1; i <= n; i++) {
    int num = min(p[i], V / w[i]);
    for (int k = 1; num > 0; k <<= 1) {
        if (k > num) k = num;
        num -= k;
        for (int j = V; j >= w[i] * k; j--)
            f[j] = max(f[j], f[j - w[i] * k] + v[i] * k);
    }
}

例题

01背包-洛谷P1164-小A点菜
(01背包)

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int a[101],f[101][10001]={0};
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;++i)cin>>a[i];
    for(int i=1;i<=n;++i)
      for(int j=1;j<=m;++j)
      {
          if(j==a[i])f[i][j]=f[i-1][j]+1;//1代表刚好可以买这道菜
          if(j>a[i]) f[i][j]=f[i-1][j]+f[i-1][j-a[i]];
          //f[i-1][j]代表不选这个菜也能恰好到j,而f[i-1][j-a[i]]就是选这道菜能到j这个价格。注意如果选这道菜能到这个价格没有方案书那么前一个f[i-1][j-a[i]]就是0了
          if(j<a[i]) f[i][j]=f[i-1][j];//不能选这道菜因为超出价格了,就沿袭悬赏一道菜的时候
      }
    cout<<f[n][m];//选第n到菜的时候,花m元的种类数
    return 0;
}

写成一维:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=10000+10;
int v[maxn],f[maxn];
int main(){
    int n,m;
    cin>>n>>m;
    f[0]=1;
    for(int i=1;i<=n;++i)    
        cin>>v[i];//读入 价值
    for(int i=1;i<=n;++i)
        for(int j=m;j>=v[i];--j)
            f[j]+=f[j-v[i]];//现在的花费+=我不点这个菜的时候的花费
    cout<<f[m]<<endl;最后把最后一个点的花费输出来就可以了
    return 0;
}

完全背包模板题-洛谷p1616
LiYuxiang 是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同种类的草药,采每一种都需要一些时间,每一种也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”

如果你是 LiYuxiang,你能完成这个任务吗?

此题和原题的不同点:

  1. 每种草药可以无限制地疯狂采摘。

  2. 药的种类眼花缭乱,采药时间好长好长啊!师傅等得菊花都谢了!

#include <cstdio>
#include <algorithm>
using namespace std;
int i,j,T,M,t[10001],p[10001],f[10001][100001];
int main()
{
    scanf("%d%d",&T,&M);
    for(i=1;i<=M;i++)
        scanf("%d%d",&t[i],&p[i]);
    for(i=1;i<=M;i++)
    {
        //从左到右
        for(j=0;j<t[i];j++)
            f[i][j]=f[i-1][j];//针对左边为数组越界的状态
        for(j=t[i];j<=T;j++)
            f[i][j]=max(f[i-1][j],f[i][j-t[i]]+p[i]);//递推
    }
    printf("%d",f[M][T]);
    return 0;
}

一维

#include <cstdio>
#include <algorithm>
using namespace std;
int i,j,T,M,t[10001],p[10001],f[100001];
int main()
{
    scanf("%d%d",&T,&M);
    for(i=1;i<=M;i++)
        scanf("%d%d",&t[i],&p[i]);
    for(i=1;i<=M;i++)
        for(j=t[i];j<=T;j++)//当需要的左边的值为数组越界时,值不变
            f[j]=max(f[j],f[j-t[i]]+p[i]);//递推
    printf("%d",f[T]);
    return 0;
}

分组背包

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值