动态规划 背包问题 最长公共子序列

2 篇文章 0 订阅

一.01背包问题

1.1 递归写法

int Dp[MXN][MXN];
//从pos开始选择总重小于wei的物品
int Rec(int pos,int wei)
{
    if(Dp[pos][wei]>=0) return Dp[pos][wei];
    int res;
    if(pos==N) res=0;
    else if(wei<W[pos]) res=Rec(pos+1,wei);
    else res=max(Rec(pos+1,wei),Rec(pos+1,wei-W[pos])+V[pos]);
    return Dp[pos][wei]=res;
}
void Fun()
{
    memset(Dp,-1,sizeof(Dp));
    printf("%d\n",Rec(0,W));
}

1.2 循环写法

1.2.1 从第i个物品之后挑选重量总和小于j的物品


int Dp[MXN][MXN];
void Fun(){
    for(int i=N-1;i>=0;--i)
        for(int j=0;j<=W;++j)
        {
            if(j<W[i])
            {
                Dp[i][j]=Dp[i+1][j];
            }
            else
            {
                Dp[i][j]=max(Dp[i+1][j],Dp[i+1][j-W[i]]+V[i]);
            }
        }
}

1.2.2 从前i个物品中选出重量不超过j的物品(从两个状态推向一个状态)


void Fun()
{
    for(int i=0;i<n;++i)
        for(int j=0;j<=W;++j)
            if(j<w[i])
                dp[i+1][j]=dp[i][j];
            else
                dp[i+1][j]=max(dp[i][j],dp[i][j-w[i]]+v[i]);
    printf("%d\n",dp[n][w]);
}

1.2.3 从前i个物品中选出重量不超过j的物品(从一个状态推向两个状态)


void Fun()
{
    for(int i=0;i<n;++i)
        for(int j=0;j<=W;++j)
        {
            Dp[i+1][j]=max(Dp[i+1][j],Dp[i][j]);
            if(j+w[i]<=W)
                Dp[i+1][j+w[i]]=max(Dp[i+1][j+w[i]],Dp[i][j]+v[i]);
        }
    printf("%d\n",dp[n][w]);
}

重复利用一个数组

int Fun(){
    for(int i=0;i<n;++i)
        for(int j=W;j>=w[i];--j)
            dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
    return dp[W];
}

01背包变形,重量的范围很大,价值的范围很小

解法


代码如下

int dp[MXN+1][MXN*MXV+1];
int Fun(){
    fill(dp[0],dp[0]+MXN*MXV+1,INF):
    dp[0][0]=0;
    for(int i=0;i<n;++i)
        for(int j=0;j<=MXN*MXV;++j)
            if(j<v[i])
                dp[i+1][j]=dp[i][j];
            else
                dp[i+1][j]=min(dp[i][j],dp[i][j-v[i]]+w[i]);
    int res=0;
    for(int i=0;i<=MXN*MXV;++i)
        if(dp[n][i]<=W)
            res=i;
    return res;
}



二.最长公共子序列(LCS)


void Fun(){
    for(int i=0;i<n;++i)
        for(int j=0;j<m;++j)
            if(s[i]==t[j])
                dp[i+1][j+1]=dp[i][j]+1;
            else
                dp[i+1][j+1]=max(dp[i][j+1],dp[i+1][j]);
    return dp[n][m];
}

三.完全背包

3.1



int Fun(){
    for(int i=0;i<n;++i)
        for(int j=0;j<=W;++j)
            for(int k=0;k*w[i]<=j;++k)
                dp[i+1][j]=max(dp[i+1][j],dp[i][j-k*w[i]]+k*v[i]);
    return dp[n][W];
}

如图可见

程序如下

int Fun(){
    for(int i=0;i<n;++i)
        for(int j=0;j<=W;++j)
            if(j<w[i])
                dp[i+1][j]=dp[i][j];
            else
                dp[i+1][j]=max(dp[i][j],dp[i+1][j-w[i]]+v[i]);
    return dp[n][W];
}

重复利用一个数组

int Fun(){
    for(int i=0;i<n;++i)
        for(int j=w[i];j<=W;++j)
            dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
    return dp[W];
}

由于dp[i+1]计算只需要dp[i]和dp[i+1],所以结合奇偶性,使用滚动数组

int Dp[2][MXW+1];
int Fun(){
    for(int i=0;i<n;++i)
        for(int j=0;j<=W;++j)
            if(j<w[i])
                dp[(i+1)&1][j]=dp[i&1][j];
            else
                dp[(i+1)&1][j]=max(dp[i&1][j],dp[(i+1)&1][j-w[i]]+v[i]);
    return dp[n&1][W];
}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值