浅谈贪心与动归

浅谈贪心与动归

初学时 想必都会对两者的认识有一些混淆
概念性质的就不赘述了
来谈谈我在刷题过程中对两者的见解(诚心接受各位的指正)
从搜索到贪心——求解算法的优化 这篇文章非常值得一看
P1478 陶陶摘苹果(升级版) 对应的oj题目

对比

贪心像是动归的一个特例
动归的核心在于:状态转移,找出那个转移方程
贪心的核心在于:局部选取最优解,并且选取的贪心策略不会影响到其他的状态

用01背包举个例子

在n件物品取出若干件放在空间为c的背包里,每件物品的体积为w1 w2...wn,与之相对应的价值为v1 v2...vn,最终使背包所装物品的总价值最高
代码如下,基本上大家都会写

int dp[i][j]; //dp[i][j] 表示取到第i个物品,背包容量为j
int pack01(int v[],int w[],int n,int c){ //value weight number capacity
    for(int i=1;i<=n;++i)
        for(int j=1;j<=c;++j){
            if(w[i]>j) dp[i][j]=dp[i-1][j];
            else dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
        }
}
空间优化 一维
for(int i=1;i<=n;i++)
    for(int j=c;j>=1;j--){ //注意从后往前
        if(w[i]<=j){ //二维变一维
            dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
        }
    }
更简洁
for(int i=1;i<=n;i++)
    for(int j=c;j>=w[i];j--){
        dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
    }

但如果将v1 v2...vn这些每个物品对应的价值都变成1(v1=v2=...=vn=1)
这样一来,每个物品的价值都相同。无论你有多重,你的价值和别人都一样
很显然,我们只要把物品的重量进行排序,先拿轻的再拿重的,结果必然最优

动归加上一个特例 就这样成为了贪心可解决的题目
并且时间复杂度O(n)直接下降到排序的nlogn

再来举个更接地气的栗子

找纸币

基本上每个国家设计的货币都是符合贪心原则的
我国的纸币面额分别为:100元、50元、20元、10元、5元、2元、1元
当需要找钱给别人时,先找大面值的纸币再找小面值的,最后一定是纸币数量最少的

但如果面额是1元、5元、11元的纸币
当你找别人15元时,按照贪心规则,找的是一张11元和4张1元的,一共5张
而正确答案显而易见是3张5元的,一共3张
这就不符合贪心策略了,这时怎么来找到最少的?这就需要用到动归了

这其实是完全背包(每件物品可以取多次)的模板
必须把背包装满,即正好找出零钱,不多也不少(会影响初始化,文末见背包九讲)
每个物品价值 v[i]=1,并且不是总价值最大,而是纸币数量最小 max->min

int m[4]={0,1,5,11};
int dp[5][20]; //dp[i][j] i表示纸币种类,j表示找回的零钱
//转移方程 dp[i][j]=min(dp[i-1][j],dp[i][j-m[i]]+1);
#define inf 10000 //若背包则是-∞
//初始化
for(int i=0;i<=3;++i)
    for(int j=0;j<=15;++j){
        if(j==0) dp[i][j]=0;
        else dp[i][j]=inf;
    }

for(int i=1;i<=3;++i){
    for(int j=0;j<=15;++j){
        if(j>=m[i]) dp[i][j]=min(dp[i-1][j],dp[i][j-m[i]]+1);
        else dp[i][j]=dp[i-1][j];
        //请注意转移方程中dp[i][j-m[i]]+1里的[i]
        //而01背包是[i-1],这是物品能否取多次的关键所在
    }
}
cout<<dp[3][15];

空间优化

int m[4]={0,1,5,11};
int dp[20];
#define inf 10000
for(int j=0;j<=15;++j){
    if(j==0) dp[j]=0;
    else dp[j]=inf;
}
for(int i=1;i<=3;++i){
    for(int j=m[i];j<=15;++j){
        dp[j]=min(dp[j],dp[j-m[i]]+1);
    }
}
cout<<dp[15];

如果是第一种问法,要求恰好装满背包,那么在初始化时除了 F[0] 为 0 ,其它
F[1..V ] 均设为 ?∞ ,这样就可以保证最终得到的 F[V ] 是一种恰好装满背包的最优解。
如果并没有要求必须把背包装满,而是只希望价格尽量大,初始化时应该将 F[0..V ]
全部设为 0 。
这是为什么呢?可以这样理解:初始化的 F 数组事实上就是在没有任何物品可以放
入背包时的合法状态。如果要求背包恰好装满,那么此时只有容量为 0 的背包可以在什
么也不装且价值为 0 的情况下被“恰好装满”,其它容量的背包均没有合法的解,属于
未定义的状态,应该被赋值为 -∞ 了。如果背包并非必须被装满,那么任何容量的背包
都有一个合法解“什么都不装”,这个解的价值为 0 ,所以初始时状态的值也就全部为 0
了。
取自《背包问题九讲》1.4 初始化的细节问题

原文地址:https://www.cnblogs.com/lidasu/p/10957495.html

时间: 2024-10-07 06:21:09

浅谈贪心与动归的相关文章

浅谈贪心策略——相邻交换

浅谈贪心策略——相邻交换 题解主要写贪心的考虑方法:相邻交换法. 我们在平时的贪心题几乎都可以正确的贪心方法. 主要思想 设交换前对答案的贡献为x,交换后对答案的贡献为y l  若x>y则不交换 l  若x<y则需交换 l  若x==y则不交换(交换反而增加时间复杂度) 作为题目,需要建立数学模型设置未知数表示x和y得到不等式从而得出排序的关键字. 例题:皇后游戏(Luogu OJ P2123) 网址: https://www.luogu.org/problemnew/show/P2123 题

浅谈贪心

序言 贪心算法是 \(\text{OI}\) 界中一门高深又基础的算法,一般用于求解最优性问题. 贪心变化多端,时易时难,例如:易的 删数问题 ,难的 树上的数 . 博主因为学业繁忙,只能抽出时间写,经常会咕咕咕,写的不好的请见谅. 参考文献:李煜东<算法竞赛进阶指南>. \(~\) 贪心是个啥? 贪心贪心,当然是贪心啦. 贪心是一种在每一次做决策的时候,总是采取当前意义下最优策略的算法. 换句话说,每次做出选择,我们总是会选择当前看来最优秀的选择. 这是一种 局部最优性 推导至 整体最优性

浅谈C++容器动态内存管理的优化

在信息学竞赛中,C++的容器的用途非常广泛,但经常因常数过大而超时.怎样才能提高它们的效率呢? 我们知道,容器是存储同一类对象的对象,既然"对象"我们无法改变,那么我们只能从"存储"入手,不难想到,不同容器在实现上的根本区别是它们对应着不同的内存组织方式,内存管理无疑是这种实现的核心,所以优化内存管理是加快容器效率的最好途径之一. 一.内存分配器简介 怎样才能优化内存管理呢?很简单,C++为我们提供了这样的接口,我们可以通过自定义容器模板中的最后一个allocato

浅谈web应用的负载均衡、集群、高可用(HA)解决方案(转)

1.熟悉几个组件 1.1.apache     —— 它是Apache软件基金会的一个开放源代码的跨平台的网页服务器,属于老牌的web服务器了,支持基于Ip或者域名的虚拟主机,支持代理服务器,支持安 全Socket层(SSL)等等,目前互联网主要使用它做静态资源服务器,也可以做代理服务器转发请求(如:图片链等),结合tomcat等 servlet容器处理jsp.1.2.ngnix     —— 俄罗斯人开发的一个高性能的 HTTP和反向代理服务器.由于Nginx 超越 Apache 的高性能和稳

浅谈算法和数据结构

: 一 栈和队列 http://www.cnblogs.com/yangecnu/p/Introduction-Stack-and-Queue.html 最近晚上在家里看Algorithems,4th Edition,我买的英文版,觉得这本书写的比较浅显易懂,而且“图码并茂”,趁着这次机会打算好好学习做做笔记,这样也会印象深刻,这也是写这一系列文章的原因.另外普林斯顿大学在Coursera 上也有这本书同步的公开课,还有另外一门算法分析课,这门课程的作者也是这本书的作者,两门课都挺不错的. 计算

浅谈移动前端的最佳实践(转)

前言 这几天,第三轮全站优化结束,测试项目在2G首屏载入速度取得了一些优化成绩,对比下来有10s左右的差距: 这次优化工作结束后,已经是第三次大规模折腾公司框架了,这里将一些自己知道的移动端的建议提出来分享下,希望对各位有用 文中有误请您提出,以免误人自误 技术选型 单页or多页 spa(single page application)也就是我们常常说的web应用程序webapp,被认为是业内的发展趋势,主要有两个优点: ① 用户体验好 ② 可以更好的降低服务器压力 但是单页有几个致命的缺点:

python浅谈正则的常用方法

python浅谈正则的常用方法覆盖范围70%以上 上一次很多朋友写文字屏蔽说到要用正则表达,其实不是我不想用(我正则用得不是很多,看过我之前爬虫的都知道,我直接用BeautifulSoup的网页标签去找内容,因为容易理解也方便,),而是正则用好用精通的很难(看过正则表的应该都知道,里面符号对应的方法规则有很多,很灵活),对于接触编程不久的朋友们来说很可能在编程的过程上浪费很多时间,今天我把经常会用到正则简单介绍下,如果不是很特殊基本都覆盖使用. 1.正则的简单介绍 首先你得导入正则方法 impo

浅谈商城活动设计

如题:浅谈商城活动设计 标题改成“浅谈商城活动的数据库设计”可能更加合理. 文章背景 为什么要吐槽,为什么要写这篇文章 本来我在弄大数据搜索,自己玩的不亦说乎,虽然感觉数据库设计不合理,但我可以数据清洗,弄到自己的搜索引擎里,自己随便玩,所以当时感觉在烂的数据库设计和我关系不大,只要我把数据清洗好,弄到自己的引擎里我的搜索正常,准确,问题不大.但忽然有一天老大跑来说ERP对接需要你来lead一下,然后一两个月带着捣乱的产品妹妹,和没有经验开发弟弟搞了ERP的简单对接,然后老大又说咱们商城库存总有

浅谈ES6原生Promise

浅谈ES6原生Promise 转载 作者:samchowgo 链接:https://segmentfault.com/a/1190000006708151 ES6标准出炉之前,一个幽灵,回调的幽灵,游荡在JavaScript世界. 正所谓: 世界本没有回调,写的人多了,也就有了})})})})}). Promise的兴起,是因为异步方法调用中,往往会出现回调函数一环扣一环的情况.这种情况导致了回调金字塔问题的出现.不仅代码写起来费劲又不美观,而且问题复杂的时候,阅读代码的人也难以理解. 举例如下