第十六周学习总结

本文介绍了背包问题的三种常见形式:01背包、完全背包和多重背包,通过动态规划方法解决,并对比了它们与贪心算法的区别。重点讲解了如何设计状态转移方程,以及在处理不同限制条件下的策略调整。适合初学者理解背包问题的动态规划应用。
摘要由CSDN通过智能技术生成

01背包问题
基本题型:背包的体积为m,有n个物品它们有不同的价值v和不同的体积w,计算背包最多能装多少价值的物品。
和贪心的区别:贪心求解背包问题用的性价比,性价比优的先装,剩下的物品可以拆分直到装满背包。动态规划01背包问题只有两种状态,1装0不装,物品不可拆分。由于只有装与不装两种状态,无需排序,但如果增加其他限制条件,也可能需要考虑排序问题。
普通方法:

memset(d,0,sizeof(d));
for(i=1; i<=n; i++)
{
        for(j=0; j<=m; j++)
        {
              if(j-w[i]>=0)
                  d[i][j]=max(d[i-1][j],d[i-1][j-w[i]]+v[i]);
              else d[i][j]=d[i-1][j];
        }
}
cout<<d[n][m]<<endl;

用d[i][j]描述状态,i表示前i件物品,j表示背包的承重。因为物品只存在放(1)与不放(0)两种状态:
0时 d[i][j]=d[i-1][j],因为第i个物品不放,相当于i-1的物品放到承重为j的背包里。
1时 d[i][j]=d[i-1][j-w[i]]+v[i]其中j-w[i]相当于为第i个物品预留这个物品的空间。


题意:有n件物品可买,你有m钱,告诉你这n件物品每件的价格pi,价值vi,以及你至少有多少钱才能买这件物品qi,求你最多能得到多少价值

我们应该先排序,把花费大的及可能买的尽量排在后面,因为这样能保证花费小的已经购买了且花费大的也能购买,但是直接这样排是不对滴,分析一下,发现如果知道a物品(pa,qa,va)和b物品(pb,qb,vb),如果你想先买a物品再买b物品,你手里至少要有pa+qb元,如果你想先买b物品再买a物品,你手里至少要有pb+qa元,比较pa+qb与pb+qa取小值,如果pa+qb<pb+qa,即qb-pb<qa-pa,先买a,按照以上排序,先买q-p大的更合适。

#include<iostream>
#include<algorithm>
#include<minmax.h>
using namespace std;
struct goods
{
    int p,q,v;
    bool operator<(const goods &b)const
    {
        return q-p<b.q-b.p;
    }
} a[505];
int d[5005],n,m,i,j;
int main()
{
    while(cin>>n>>m)
    {   memset(d,0,sizeof(d));
        for(i=1; i<=n; i++)
            cin>>a[i].p>>a[i].q>>a[i].v;
        sort(a+1,a+1+n);
        for(i=1; i<=n; i++)
        {   for(j=m; j>=a[i].q; j--)
                d[j]=max(d[j],d[j-a[i].p]+a[i].v);
        }
        cout<<d[m]<<endl;
    }
    return 0;
}

完全背包问题

基本题型:背包的体积为m,有n类物品它们个数不限,每类物品有不同的价值v和不同的体积w,计算背包最多能装多少价值的物品。
和01的区别:01的个数限,每类只有一件,要么取要么不取,完全的个数不限,每类可以取1件或多件或不取。
普通方法:

memset(d,0,sizeof(d));
for(i=1; i<=n; i++)
{
        for(j=0; j<=m; j++)
        {
              for(k=0;k*w[i]<=j;k++)
              {    if(j-w[i]>=0)
                      d[i][j]=max(d[i-1][j],d[i-1][j-k*w[i]]+k*v[i]);
                  else d[i][j]=d[i-1][j];
              }
        }
} 
cout<<d[n][m]<<endl;

题意:给出本金和年数,又给出几种股票的价钱和利息,求最大本利和,每种股票可以多次购买。(债券的价值总是1000美元的倍数。债券的利息从来不超过其价值的10%。)

债券的价值是1000美元的倍数,所以存数组的时候可以先除以1000,以防最后数太大超范围,每年的利息要单独求,求得的利息加上原来的钱作为下一次的本金。它的的特点是:把上期末的本利和作为下一期的 本金,在计算时每一期本金的数额是不同的。主要应用于计算多次等额投资的本利终值和计算多次等额回款值。

#include<iostream>
#include<cstring>
using namespace std;
struct z
{
    int v,l;
} a[11];
int d[100005],i,j,k;
int t,m,n,p,sum;
int main()
{
    cin>>t;
    while(t--)
    {
        cin>>m>>n>>p;
        for(i=1; i<=p; i++)
        {   cin>>a[i].v>>a[i].l;
            a[i].v=a[i].v/1000;
        }
        sum=m;
        for(i=1; i<=n; i++)
        {   m=sum/1000;
            memset(d,0,sizeof(d));
            for(j=1; j<=p; j++)
            {   for(k=a[j].v; k<=m; k++)
                {
                    d[k]=max(d[k],d[k-a[j].v]+a[j].l);
                }
            }
            sum+=d[m];
            m=sum;
        }
        cout<<sum<<endl;
    }
    return 0;
}

多重背包问题

基本题型:有n种物品和一个容量为m的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大
和完全背包的区别:每类不是无限件,而是限制了件数,一般大于一件。
普通方法:

memset(d,0,sizeof(d));
for(i=1; i<=n; i++)
{
        for(j=0; j<=m; j++)
        {
              for(k=0;k<=n[i]&&k*w[i]<=j;k++)
              {    if(j-w[i]>=0)
                      d[i][j]=max(d[i-1][j],d[i-1][j-k*w[i]]+k*v[i]);
                  else d[i][j]=d[i-1][j];
              }
        }
} 
cout<<d[n][m]<<endl;

分组背包

题意:有S款运动鞋,一个n件,总钱数为m,求不超过总钱数且每款鞋子至少买一双的情况下,使价值最大。如果有一款买不到,就输出“Impossible"。

把所有鞋子按品牌分组,每一组里的鞋子单独看成一种,即按01背包求。

#include<iostream>
#include<minmax.h>
using namespace std;
int N,M,K,i,j,k;
int d[10005][10005];
//产品总数,钱,品牌数
struct p
{
    int a,b,c;//品牌号,价格,价值
} x[100005];
int main()
{
    while(cin>>N>>M>>K)
    {   for(i=1; i<=N; i++)
            cin>>x[i].a>>x[i].b>>x[i].c;
        for(i=0; i<=K; i++)
        {
            for(j=0; j<=M; j++)
            {
                if(i==0)//品牌数为0,无论有多少钱,最大价值都是0;
                    d[i][j]=0;
                else d[i][j]=-1;
            }
        }
        for(i=1; i<=K; i++)//品牌数(组数)循环
        {
            for(j=1; j<=N; j++)//产品总数
            {
                if(x[j].a==i)//如果是该组成员
                {
                    for(k=M; k>=x[j].b; k--) //01思想,买或不买
                    {   d[i][k]=max(d[i][k],d[i][k-x[j].b]+x[j].c);
                        d[i][k]=max(d[i][k],d[i-1][k-x[j].b]+x[j].c);
                    }
                }
            }
        }
        if(d[K][M]<0) cout<<"Impossible"<<endl;
        else
            cout<<d[K][M]<<endl;
    }
    return 0;
}

总结:本周学了背包dp,将ppt上的又看了一下,有些地方还是不懂。由于考试,博客看的也不多,两周看这些东西确实太少了。对于做dp的题,比着一些基本模板还能做做简单题目,但还是不会分析,状态转移方程还是不会,确实dp需要加大时间的才能理解消化。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值