学习记录 3.1 DP再谈.背包

记录(干货还是有一些的) 最近三天没怎么做题(只看理论不敲代码是不行的...orz...),看了一些关于DP的文章,主要是关于背包的相关问题。

着重看了01背包和完全背包。[还捎带看了些dfs和bfs,裸题看了看,能基本复写代码,完全不会优化(记忆化算吗...嘿嘿嘿),剪枝啥的好遥远...

发现DP有三种基本的方法:1.记忆化搜索(递归)2.通过递推规律(正向、反向)来DP 3.通过状态转移来DP

w[i+1]=F{f1(w[i]),f2(w[i]),...,fk(w[i])}; (w[i]为i时状态,fj()为对某状态的第j种决策,F{}为取不同决策结果中最优的函数(规划))

这个方程是分析DP问题的关键吗,从这个基础上可以进行优化(主要的优化是对空间的优化,因为能写出状态转移方程来的话,一般都能找到不用递归的方法,前提是不懒...,所以时间上的优化不太常见,唯一见到过的是01背包中数据限制改变导致的策略调整)空间上优化主要手段是重复利用数组(这个好像在递归调用中也可以,不过没见到相关的题就不瞎说了),例如在01背包和完全背包中dp[i][j]中因为i(不同种类物品的编号)在变化的过程中原来的不用再记录了故重新赋值给相同j的从而把二维数组压缩成一维数组,还有就是如果状态转移方程中是i到i+1这种相邻状态转移到话可以考虑区分奇偶(位运算&1即可),每次转移都释放然后重新赋值,用滚动数组来做。

就这样,总的来说没怎么刷题,以后熟练了系统整理一下DP的相关知识,在这就先不放具体的问题和代码了。

算了还是放一下吧...

重复利用数组优化空间:

#include<cstdio>
#include<algorithm>
const int M=10000;
//01背包
int dp[M+10];//dp[i+1][j]装前j种物品质量为j的物品可能的最大的价值
//v[i]、w[i]为编号为i的物品的价值量和质量,W为限重
void solve()
{
    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]);
    printf("%d\n",dp[W]);
}
//完全背包,沿用01背包的记号,注意递推的方向是不同的
int dp[M+10];//dp[i+1][j(同01背包)装前j种物品质量为j的物品可能的最大的价值
//一开始的状态转移方程是带k的(挑选特定一种物品的个数),通过恒等变形推过来的,和01背包的思路不完全相同,时间复杂度是O(n*W)
void solve()
{
    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]);
    printf("%d\n",dp[W]);
}

滚动数组

//完全背包中,利用奇偶性结合位与,使用滚动数组优化空间
int dp[2][M+10];
void solve()
{
    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)&1][j],dp[i+1][j-w[i]]+v[i]);
    }
    printf("%d\n",dp[n&1][w]);
}

 

                

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值