浅谈OI中的动态规划

啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊我dp菜死了快来补一下。基本没讲解,来就放代码,偶尔写两句,也是在瞎扯。

简介

动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法。20世纪50年代初美国数学家R.E.Bellman等人在研究多阶段决策过程(multistep decision process)的优化问题时,提出了著名的最优化原理(principle of optimality),把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解,创立了解决这类过程优化问题的新方法——动态规划。

背包问题

背包是一类经典dp。

01背包

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

关于初始化:

如果要求了把背包放满,那么初始化f[0]=0,f[1~n]=+INF。

如果没有要求放满,初始化为0。

所有的背包问题基本都这样初始化。

我们把j从m循环到w[i]:

1.寻找放了w[i]后剩余空间的最大值。

2.从大到小循环,保证当前状态的上一个状态不会出现已有当前物品的情况。

#include<cstdio>
#include<algorithm>
using namespace std; 
int n,m,w[205],c[205],f[205];
int main()
{
    scanf("%d%d",&m,&n);
    for(int i=1;i<=n;i++)
    scanf("%d%d",&w[i],&c[i]);
    for(int i=1;i<=n;i++)
    for(int j=m;j>=w[i];j--) 
    f[j]=max(f[j],f[j-w[i]]+c[i]);
    printf("%d\n",f[m]);
    return 0;
}

完全背包

有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

和0~1背包的唯一区别就是有可能一个物品放多种。

反正是同样的背包,你换一换第二维循环顺序不就行了吗?这样其实就是从绝不可能重复到可能重复,刚好满足完全背包的要求,理解清了0/1的循环顺序,这个也就没问题了。

#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,w[205],c[205],f[205];
int main()
{
    scanf("%d%d",&m,&n);
    for(int i=1;i<=n;i++)
    scanf("%d%d",&w[i],&c[i]);
    for(int i=1;i<=n;i++)
    for(int j=w[i];j<=m;j++)
    f[j]=max(f[j],f[j-w[i]]+c[i]);
    printf("max=%d\n",f[m]);
    return 0;
}

多重背包

有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

这个题可以用二进制拆分思想来做,把每一个n[i]的系数化为1,2,4,...,2^(k-1),n[i]-2^k+1。

假如有一个n[i]=13,那么13=(2^0)+(2^1)+(2^2)+13-(2^3)+1,k在此处为3。

这样可以保证0~n[i]区间的每一个整数都可以用这些系数相加得到。

所以该问题能转化为0/1背包。

注意处理n-(2^k)+1。

多重背包复杂度O(vΣNi=1logn[i])

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std; 
int n,type,m,v,wt,num;
int T;
struct node
{
    int w=0,c=0;
};
int f[1005];
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        n=0;
        node a[1005];
        scanf("%d%d",&m,&type);
        while(type--)
        {
            scanf("%d%d%d",&v,&wt,&num);
            int p=1;
            while(num-p>0)
            {
                num-=p;
                a[++n].w=v*p;
                a[n].c=wt*p;
                p<<=1;
            }
            a[++n].w=num*v;
            a[n].c=wt*num;
            //注意要处理n[i]-(2^k)+1这个情况 
        }
        memset(f,0,sizeof(f));
        for(int i=1;i<=n;i++)
        for(int j=m;j>=a[i].w;j--)
        f[j]=max(f[j],f[j-a[i].w]+a[i].c);
        printf("%d\n",f[m]);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/valentino/p/11740673.html