-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcontent.json
More file actions
1 lines (1 loc) · 177 KB
/
content.json
File metadata and controls
1 lines (1 loc) · 177 KB
1
[{"title":"系统分析与设计(六)","date":"2020-11-12T09:00:56.400Z","path":"2020/11/12/系统分析与设计(六)/","text":"系统分析与设计(六) 使用 UMLet 建模 使用类图,分别对 Asg_RH 文档中 Make Reservation 用例以及 Payment 用例开展领域建模。然后,根据上述模型,给出建议的数据表以及主要字段,特别是主键和外键 注意事项: 对象必须是名词、特别是技术名词、报表、描述类的处理; 关联必须有多重性、部分有名称与导航方向 属性要注意计算字段 数据建模,为了简化描述仅需要给出表清单,例如: Hotel(ID/Key,Name,LoctionID/Fkey,Address……) Make Reservation 用例 12345Hotel(ID/key, Name, address, starRating, price)Room(ID/key, type, availability)customer(ID/key, fullName, emailAddress)shoppingBasket(ID/key)Reservation(ID/key, checkInDate, checkOutDate, price, numberofnights, requried, numberOfAdult, numberOfChildren, smoking) Payment 用例 1234Payment(ID/key, totalPrice, reservationID/Fkey, cardID/Fkey)ReservationItem(ID/key, paymentID/Fkey,checkInDate, ...)CreditCard(ID/key, Type, CardSecurityCode, ExpiryDate, CardHolderID/Fkey)CardHolder(ID/key, Title, FirstName, LastName, Address1, Address2, City, CountyOrState, Country, Postcode, DaytimeTelephone, EveningTelephone) 使用 UML State Model,对每个订单对象生命周期建模 建模对象: 参考 Asg_RH 文档, 对 Reservation/Order 对象建模。 建模要求: 参考练习不能提供足够信息帮助你对订单对象建模,请参考现在 定旅馆 的旅游网站,尽可能分析围绕订单发生的各种情况,直到订单通过销售事件(柜台销售)结束订单。 Reservation","tags":[{"name":"SSAD","slug":"SSAD","permalink":"https://zhangziquan.github.io/tags/SSAD/"}]},{"title":"Hello World","date":"2020-11-12T09:00:55.533Z","path":"2020/11/12/hello-world/","text":"Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub. Quick Start Create a new post 1$ hexo new \"My New Post\" More info: Writing Run server 1$ hexo server More info: Server Generate static files 1$ hexo generate More info: Generating Deploy to remote sites 1$ hexo deploy More info: Deployment","tags":[]},{"title":"博客重启计划","date":"2020-05-25T09:06:00.000Z","path":"2020/05/25/博客重启计划/","text":"博客重启计划 时隔快一年重新把这个博客整一整。。。 之前先是域名过期跪了,换回了github的域名访问。 然后发现live2d挂了,重新修复了一下,计划弄些新的live2d过来。 接下来就是随缘日常更新一下,相关论文解析以及某些技术向问题之类的。 emoticon_1","tags":[{"name":"Daily","slug":"Daily","permalink":"https://zhangziquan.github.io/tags/Daily/"}]},{"title":"Weakly Supervised Salient Object Detection Using Image Labels 解析","date":"2019-07-18T07:00:47.000Z","path":"2019/07/18/Weakly Supervised Salient Object Detection Using Image Labels 解析/","text":"Weakly Supervised Salient Object Detection Using Image Labels Abstract 这一篇论文提出了一个关于显著对象检测的一种新的优化框架,能够在以前常用的无监督学习下使用,能够显著提高性能,并有着较快的速度。以及将CRF运用在模型上,用于降噪,使用CAM进行对象定位。 Problem 在目前的显著对象检测中,往往使用非监督方法,采用的都是一些基于低级特征的方法,无法处理多类图片,通常存在2个问题: 缺少空间相关性推断,位置附近的像素 无法检测高语义特征,因为某些物体在低语义特征(颜色,大小,纹理)上和其它对象对比度较低,因此要进行检测需要高语义特征支持。 而采用监督学习后,因为有了有效的标记,能够在训练中学习到显著对象的特征规则,忽略了干扰的噪声,所以在效果上能够有极大提升,但是也存在几个问题: 有监督学习同时也带来了过拟合问题,泛化性变弱。 监督学习的数据集需要进行标记且整个过程耗时耗费人力物力。 因此本文就监督学习的两个问题给出了解决方案。 Network network 整个网络的主要流程就是使用无监督学习的检测方法,生成初始预测显著性图,用原图像对Multi-FCN进行训练,得到CAM图以及FCN预测的显著性图,这三个图通过CRF模型,进行综合增强,得到第二轮的显著性概率图。 就框架中的3个比较重要的地方展开 Multi-FCN multi-FCN 作为监督学习的一个重要的部分,FCN是CNN上的拓展,与CNN相比,FCN将全连接层全部换成deconv(反卷积,最通常的叫法是上采样)即对最后一个卷基层的特征图进行上采样,使其恢复到与输入相同的尺寸,从而对每个像素都能进行预测。(在这里FCN使用多个只是为了能够检测不同比例的图像的视觉对比。) 论文中的FCN主要任务是输出分类任务和以及输出逐像素的显著图,这里体现了监督学习,因为通过分类任务的训练,能够通过高语义特征的划分来区分不同对象,这样能够纠正在无监督学习下,因为语义信息模糊的规则,如在低特征上无法区分的对象,也能检测出来了。 CAM CAM CAM即类激活映射,一开始是作为对分类问题的网络,提供关注区域(提供依据),在对卷积层结果进行全局池化后,能够进行特征图加权求和得到对某一类的热力图,这样就能得知模型是通过哪些像素来确定图片中的物体的属于哪一类的。 CAM得到的热力图相对于FCN的逐像素预测图来说相对没这么准确,但是对于对象位置的定位十分准确,并且在泛化性上表现很好,对于一些未知类别的图像,也能够对显著对象进行准确的定位,所以可以加入到CRF中,为第二轮生成更加精确的预测图做一个引导。 个人认为是因为CAM在FCN训练过程中,在一些背景等无关信息上得到了训练,所以不会过度关注这些地方。 CRF模型 CRF是这个框架中的核心部分,即条件概率分布模型,在文章中只提到了能够通过空间相对位置,以及显著对象定位来纠正一些错误的预测。这个模型是一个概率图模型,用于求联合概率。即在上文提到的,无监督学习的一个缺点就是没有考虑空间一致性(鼓励附近具有相似颜色的像素采用相似的显着性概率),因此在通过CRF后,能得到更具有空间相关性的概率图。 在这里为了让未知的图像能够有更好的预测效果,使用无标记的数据集进行微调,即让CAM来指导预测图,在上面已经提到过CAM对于未知图像有比较好的定位效果。 Result 在表现上超越许多非监督学习模型,并且能够和许多监督学习的模型相比较。证明了在泛化性上是表现很好的,能够和监督学习模型相比也说明了这一弱监督学习的成功。 Ablation Studies 在模型检测测试当中,可见CAM和CRF的作用十分重要,单使用FCN的预测效果仅能和原来的无监督学习相比,而增加了CRF,能够增强空间相关性,解决了第一个问题。 而CAM的加入提供了定位以及对模型的引导有一个泛化作用,并且是强监督学习变为了弱监督学习,能让FCN训练有素,避免了过拟合,而且能够纠正一些错误的预测,其中包含大量的网络参数能够发现大规模样本中某些潜在规则,从而进行降噪。 Related Deep Unsupervised Saliency Detection: A Multiple Noisy Labeling Perspective 在2018年的CVPR中,同样有一篇论文采取了类似的使用无监督学习作为初始化思路,使用伪监督学习,以此来进行监督学习并且避免了过拟合的问题。 文章认为传统的无监督学习由于独立数据集,使得在自然环境中的泛化性较强,并且有一些噪音也有一定的作用,因此使用传统无监督学习得到的结果作为ground truth + noisy, 作为监督来同时训练一个显著性检测模块和噪声模块,在一定程度上也取得了一定的效果。","tags":[{"name":"Salient Object Detection","slug":"Salient-Object-Detection","permalink":"https://zhangziquan.github.io/tags/Salient-Object-Detection/"},{"name":"Supervised","slug":"Supervised","permalink":"https://zhangziquan.github.io/tags/Supervised/"}]},{"title":"系统分析与设计个人总结","date":"2019-06-23T14:53:00.000Z","path":"2019/06/23/系统分析与设计个人总结/","text":"系统分析与设计个人总结 学号 昵称 16340296 缘ね花落 个人总结 我在团队中的角色是技术经理和前端开发人员,负责前端的开发和需求,主要是设计和逻辑实现,主要的工作就是写前端,顺便搞搞UI设计,弄一些好看的效果。 作为技术经理 解决其它成员遇到的一些问题,写好组件方便其它设计成员使用,写出例子。 对其它成员提出的一些需求进行技术评估,并构思实现方法,寻找方法,争取使用最有效、简洁的方法去实现。 作为前端开发人员 使用mpvue框架来编写开发任务的列表显示,实现问卷创建的UI设计,做到问卷对客户友好,便于客户查看任务订单。 分析设计,进行实现。 PSP 2.1 表格 PSP2.1 Personal Software Process Stages Time (%) Planning 计划 10 · Estimate 预估任务时间 10 Development 开发 80 · Analysis 需求分析(包括自我学习) 12 · Design Spec 生成设计文档 8 · Design Review 设计复审 3 · Coding Standard 代码规范 6 · Design 具体设计(绘制UI, 架构设计等) 8 · Coding 具体编码 25 · Code Review 代码复审 3 · Test 测试(自我测试,修改代码,提交修改) 15 Reporting 报告 10 · Test Report 测试报告 2 · Size Measurement 计算工作量 2 · Postmortem & Process Improvement Plan 事后总结, 并提出过程改进计划 6 主要工作清单 最得意/有价值的工作清单:实现问卷的创建UI,参考了很多问卷设计的页面,给出一套非常清新,方便操作的界面,对用户友好UP 最苦劳的工作清单:做任务列表的list,需要考虑到各种情况,并且刷新频繁,很容易就会出bug,也是耗时最多的地方,并且因为UI的问题,认真设计了好久,但觉得也就那样。 在项目相关仓库中的贡献 个人博客清单 mpvue前端小技巧 谈谈mpvue组件化的重要性 数据库查询的高级技巧 特别致谢 感谢 16340293-Tuid 同学,为了我们的数据库处理和调用,付出了很多。 感谢 16340294-Duke 和 16340319-zhuangwz 同学,督促我们的开发进度,使开发保持正常进度。","tags":[{"name":"SSAD","slug":"SSAD","permalink":"https://zhangziquan.github.io/tags/SSAD/"}]},{"title":"mpvue前端小技巧","date":"2019-06-23T13:14:00.000Z","path":"2019/06/23/mpvue前端小技巧/","text":"mpvue mpvue 是一个使用 Vue.js 开发小程序的前端框架,目前支持 微信小程序、百度智能小程序,头条小程序 和 支付宝小程序。 框架基于 Vue.js,修改了的运行时框架 runtime 和代码编译器 compiler 实现,使其可运行在小程序环境中,从而为小程序开发引入了 Vue.js 开发体验。 一些使用微信小程序前端/Mpvue开发前端的小技巧 Swiper的高度调整 在做小程序开发的时候,往往都要用到列表来进行展示,而对于列表类型的切换,则有很多各种各样的方法,有的使用按钮来切换,有的则使用滑动等方法,而swiper轮播视图则被很多人使用作为列表切换。 但是swiper存在一个比较不好的地方就是它是主要作为轮播一些图片的,因此高度需要自己设定,并不适合作为列表来使用。 但如果一定要作为列表使用的话,则有两种方法 先获得列表中的元素个数,并且元素的高度要是统一的,从而计算得到高度。 内部先放一个scroll-view,使得内部的元素能够进行滚动(推荐) 使用v-for自己做一个显示列表 mpvue里的v-for是一个很好用的东西,可以把数据中的一个数组对应为一组元素来进行渲染。 先使用v-for来写好tab,和list,最好做成组件的形式,提高代码复用性 123456789101112131415<template><div class=\"weui-tab\"> <div class=\"weui-navbar\"> <div v-for=\"(item,index) in tabs\" :key=\"index\" :class=\"{'weui-bar-item-on':activeIndex == index }\" class=\"weui-navbar__item\"> <div class=\"weui-navbar__title\">{{item}}</div> </div> </div> <div class=\"weui-navbar-content__slider\"> <div class=\"weui-navbar-slider\" v-for=\"(item,index) in tabs\" :key=\"index\"> <div class=\"weui-navbar-slider-inner\" :class=\"{'weui-navbar-slider-on':activeIndex == index}\"></div> </div> </div></div></template> 以上是tab为点击切换列表,同理列表也是一样使用v-for渲染。 tab的切换则是通过对class进行绑定,并且能够根据判断条件来进行,比如tab点击选项后,被点击的选项下面出现的下划线和颜色变化,则是通过:activeIndex == index来判断实现的。 添加使用触摸滑动的方式切换。 如果点击的方式切换则没有这么友好,因此我们可以使用对整个list的触摸进行监听控制。当发现手指触摸向左/向右之后,进行切换。 实现方式: 在主页面上绑定触摸事件 1<div class=\"mission-main\" @touchstart=\"touchStart\" @touchend=\"touchEnd\" @touchmove=\"touchmove\"> 得到触摸的开始位置和结束位置,进行判断,设置阈值防止误触。 1234567891011touchStart(e) { this.startX = e.mp.changedTouches[0].pageX; this.startY = e.mp.changedTouches[0].pageY;},touchmove(e) { this.endX = e.mp.changedTouches[0].pageX; this.endY = e.mp.changedTouches[0].pageY; if (Math.abs(this.endY - this.startY) < 100) { if (Math.abs(this.endX - this.startX) > 100) {.....switch() 添加左右页面来进行更加流畅的滑动 这个是理论上的实现,先在页面滑动页面上左右两边设置相同大小的页面隐藏在两边,类似于侧边栏,滑动能出来的那种,当向一遍滑动的时候,修改style样式中的left/right,让他能够显示出来,并且通过z-index设置,让它像滚筒一样最左边的那一张移动到最右边去。left-middle-right => middle-right-left,就能无限循环。(我猜swiper是这样弄得? 父子组件之间的交互(Props, 事件通信) 在使用mpvue之后,因为组件化更加彻底,出现很多组件需要进行相互通信的时候。 父组件给子组件传递数据 需要使用props传递数据,父组件使用绑定类似的方式。 1<children :data=\"data\"> 子组件声明该变量为props 123export default { props: ['data'] } 因为这个传递是单向的,所以在子组件中最好不要对props的数据进行修改,应当另外声明一个变量赋值后再修改使用。 如果需要数据同步,在子组件中要用到watch方法: 12345678watch: { role:function (oldValue, newValue){ this.role = newValue }, activeIndex:function (oldValue, newValue){ this.activeIndex = newValue }} 子组件传递数据给父组件 子组件使用$emit来触发父组件的自定义事件。 1this.$emit('confirmSend',data); 父组件绑定事件,在触发的时候,得到数据 123456<questionnaire @confirmSend=\"addQues\"></questionnaire>...addQues: function(val) { this.temDatas.push(val); console.log(this.temDatas) } 父组件调用子组件内部的方法 123<child ref=\"child\"></child>...this.$.refs.child.childFn() v-if和v-show的区别 在使用mpvue的时候常常会遇到v-if和v-show这两个东西来控制页面的显示。 v-if 是“真正的”条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。 v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。 而 v-show 则是简单地基于 CSS 进行切换,不管怎样都会进行渲染。 一般来说, v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件不太可能改变,则使用 v-if 较好。 自定义组件click的处理 在mpvue中,不支持对组件的click操作,因此如果想要实现点击,需要换一个思路。 单纯地想实现点击事件,则只需要在组件内绑定click就行了,若需要反馈只要使用以上的消息传递即可。 只是想得到被点击的内容,如果是这样的话,往往都是只想知道自己点击的是第几个,那么使用v-for来进行配合,直接绑定在列表上,将会得到index,然后根据自己的需要得到内容(常用于导航栏的跳转和列表详情跳转) 123456<div class=\"mission-list-item\" v-for=\"(item, index) in show_list\" :key=\"index\" v-on:click=\"goto(index)\"/> 在mpvue下如何解决img变形问题 因为在mpvue的方式是通过对vue.js编译实现的,所以在对于<img>这一标签是被编译成小程序中的<image>,而这一标签没有width\\height属性,因此我们需要写成内联的形式。 1<img class=\"icon\" style=\"width: 200px;\" mode=\"widthFix\" src=\"/static/images/question/单选.png\"> 另外image的mode里包含了对于图片的裁剪缩放模式,能够防止因尺寸不对图片变形。 云开发数据库API不支持类似SQL中like模糊查询 在小程序开发社区中有解决方法: 12345where({ key:{ $regex:'.*' + keyWord, } }) 类似于SQL中的 12select * from tablewhere key like '%'+keyWord 解决了我在数据库中检索数组中是否包含某一数值的难题。 据说后续会提供模糊搜索的API,但我貌似没有发现有。 Style的scope和!important 这个是关于style的域,scope是为了某一界面的class和另一界面的class同名导致冲突/污染,能够有效地限制style仅在该页面上使用。 而!important则是为了在让某一样式能够覆盖掉另一个同名样式,类似于一个组件有2个样式表,且存在同名的属性,这样在你所需要的属性后加上!important,则无论该样式作用先后的顺序,都能渲染你所需要的属性。(原本是后作用的样式表会覆盖前面的属性) 至于有一些在网上很多通用的模板,如果想要修改里面的样式,看中某一点,就可以使用这种覆盖的方式,但是推荐直接拿到源码来进行魔改。。。 例如使用npm下载的,在node_module中找到那个包,找到所需的组件,拉出来直接魔改更加方便。","tags":[{"name":"mpvue","slug":"mpvue","permalink":"https://zhangziquan.github.io/tags/mpvue/"},{"name":"Wechat","slug":"Wechat","permalink":"https://zhangziquan.github.io/tags/Wechat/"},{"name":"html","slug":"html","permalink":"https://zhangziquan.github.io/tags/html/"}]},{"title":"系统分析与设计(五)","date":"2019-05-24T02:43:00.000Z","path":"2019/05/24/系统分析与设计(五)/","text":"系统分析与设计(五) 使用 UMLet 建模: 根据订旅馆建模文档,Asg-RH.pdf: 绘制用例图模型(到子用例) 给出 make reservation 用例的活动图 SSAD_5_make_reservation 根据课程练习“投递员使用投递箱给收件人快递包裹”的业务场景 分别用多泳道图建模三个场景的业务过程 根据上述流程,给出快递柜系统最终的用例图模型 用正常色彩表示第一个业务流程反映的用例 用绿色背景表述第二个业务场景添加或修改的用例,以及支持 Actor 用黄色背景表述第三个业务场景添加或修改的用例,以及支持 Actor","tags":[{"name":"SSAD","slug":"SSAD","permalink":"https://zhangziquan.github.io/tags/SSAD/"}]},{"title":"系统分析与设计(四)","date":"2019-05-24T02:10:00.000Z","path":"2019/05/24/系统分析与设计(四)/","text":"系统分析与设计(四) 简答题 用例的概念 提供了一个或多个成功/失败场景,该场景说明了系统是如何和最终用户或其它系统互动,用来描述参与者如何使用系统来实现其目标。 用例和场景的关系?什么是主场景或 happy path? 用例是一个或多个场景的表示,场景则是一个参与者(Actor)向系统做出请求,系统根据参与者的请求,在不同的条件下,执行某一行为序列。而用例则包含多个场景。 主场景则是用户最常用,直接地实现用户目标的一个场景,描述了当各项工作都正常进行时用例的工作方式,所以又被称为happy path。 用例有哪些形式? 摘要 Brief (high level):简单的一段式摘要,通常是主要成功案例。用于在早期需求分析中,快速了解主题和范围。 简便格式 Casual:非正式段落格式,涵盖各种场景的多个段落。 完全 Fully:所有步骤和变化都写得很详细,并有支持部分,如前提条件和成功保证。 对于复杂业务,为什么编制完整用例非常难? 因为对于复杂业务来说,用例场景比较多,场景较为复杂,很难完全地考虑到所有的子用例和场景并且在绘制图时,可能因为用例图过于繁杂而会出错。 什么是用例图? 由参与者(Actor)、用例(Use Case),边界以及它们之间的关系构成的用于描述系统功能的视图。用例图是外部用户(参与者)所能观察到的系统功能的模型图。 描述系统上下文,显示系统的边界,以及与系统交互的外部对象,并展示系统如何去使用它们。通常作为一个总结系统和用户行为的交流工具。 用例图的基本符号与元素? 参与者(Actor):表示系统用户,即与应用进行交互的用户 用例(Use Case):表示对系统提供的功能和服务描述 用例之间的关系: 包含关系(include):用例可以简单地包含其他用例具有的行为,并把它所包含的用例行为作为自身行为的一部分。箭头由基础用例(Base)指向被包含用例(Inclusion) 扩展关系(Extend):在一定条件下,把新的行为加入到已有的用例中。 泛化关系(Generalization):一个父用例可以被特化形成多个子用例。 关联关系(Association):参与者与用例之间的关系 用例图的画法与步骤 选择系统边界 该系统是软件应用,硬件再加上参与者或者是整个组织? 确定主要参与者 参与者通过使用系统服务来实现目标 确定每个主要参与者的目标 定义满足用户目标的用例;并根据这些目标来进行命名。 用户目标级用例将与用户目标一对一,但至少有一个例外,将进行检查。 用例图给利益相关人与开发者的价值有哪些? 对于利益相关人来说,用例图可以直观地显示整个系统的功能,服务以及用户的行为结果,能够使系统按照其需求进行开发。 对于开发者来说,用例图能够细化用户的需求,软件的服务要求等,通过场景来理解软件的目标,使软件架构设计思路更加清晰。 建模练习题(用例模型) 选择2-3个你熟悉的类似业务的在线服务系统(或移动 APP),如定旅馆(携程、去哪儿等)、定电影票、背单词APP等,分别绘制它们用例图。并满足以下要求: 请使用用户的视角,描述用户目标或系统提供的服务 粒度达到子用例级别,并用 include 和 exclude 关联它们 请用色彩标注出你认为创新(区别于竞争对手的)用例或子用例 尽可能识别外部系统和服务 然后,回答下列问题: 为什么相似系统的用例图是相似的? 因为在相似的系统中,系统的功能和服务都是大致相同的,基础功能差不多,所以主场景往往也相似,用例图所以是相似的。 如果是定旅馆业务,请对比 Asg_RH 用例图,简述如何利用不同时代、不同地区产品的用例图,展现、突出创新业务和技术 根据旅客的要求,对酒店周围环境的要求来进行旅馆的推荐,以及可以通过选择旅行路线,景点路线来推荐相关的酒店。根据不同地区的旅馆的特色,如果符合用户的爱好则可以进行推荐。 如何利用用例图定位创新思路(业务创新、或技术创新、或商业模式创新)在系统中的作用 在用例图中,可以给创新的模块进行颜色的高亮处理,标记出与之相关的子用例,行为过程,和产生的结果,观察在用例图中的影响。 请使用 SCRUM 方法,选择一个用例图,编制某定旅馆开发的需求(backlog)开发计划表 ID Name IMP Est How to Demo Notes 1 注册 6 3 用户可以通过手机号注册,或者是使用微信/支付宝注册绑定手机号 当用户使用微信注册后,需要绑定手机 2 登陆 6 4 用户通过手机号/邮箱/微信/支付宝/昵称进行登陆 需要进行微信/支付宝支持 3 搜索酒店 8 4 通过选择条件如地点/时间/人数/价格/星级来搜索酒店 要求结果按照用户优先选择的条件排序 4 预定房间 10 6 预定房间要先判断对应房型是否有空 5 支付订单 8 6 下单后,对房间进行锁定,若15分钟内未支付,则取消订单并解锁 6 取消订单 7 5 取消订单后,对应的房间能够解锁 7 查询订单 6 3 能够查询之前完成/取消/正在支付的订单 根据任务4,参考 使用用例点估算软件成本,给出项目用例点的估算 用例 事物 计算 原因 UC权重 注册 1 1 通过手机号注册,或者是使用微信/支付宝注册绑定手机号 简单 5 登陆 2 2 通过手机号/邮箱/微信/支付宝/昵称进行登陆 简单 5 搜索旅店 11 8 通过选择条件进行搜索 复杂 15 预定房间 9 7 判断对应房型是否有空 复杂 15 支付订单 5 4 支付限定时间 平均 10 取消订单 2 2 取消订单,解锁房间 简单 5 查询订单 2 2 查询历史订单 简单 5","tags":[{"name":"SSAD","slug":"SSAD","permalink":"https://zhangziquan.github.io/tags/SSAD/"}]},{"title":"系统分析与设计(三)","date":"2019-04-15T12:48:00.000Z","path":"2019/04/15/系统分析与设计(三)/","text":"系统分析与设计(三) 简答题 简述瀑布模型、增量模型、螺旋模型(含原型方法),并分析优缺点 从项目特点、风险特征、人力资源利用角度思考 瀑布模型 瀑布模型(Waterfall Model) 是一个项目开发架构,开发过程是通过设计一系列阶段顺序展开的,从系统需求分析开始直到产品发布和维护,每个阶段都会产生循环反馈,因此,如果有信息未被覆盖或者发现了问题,那么最好 “返回”上一个阶段并进行适当的修改,项目开发进程从一个阶段“流动”到下一个阶段。在软件开发中,它往往是较少迭代和灵活的方法之一,因为在概念,启动,分析,设计,构建,测试,部署和维护阶段,进展主要在一个方向(“向下”,如瀑布)流动。 waterfall_model 优点: 定义了软件开发基本流程与活动,适用于需求和范围固定,产品本身坚固稳定,技术清晰理解的项目。 在软件生产周期的早期花费的时间可以降低后期的成本。 提供了一种结构化的方法,模型本身通过离散的,易于理解和可解释的阶段线性地进展,因此易于理解 强调文档以及源代码,如果存在完整工作的设计文档,新的团队成员甚至全新的团队应该能够通过阅读文档来熟悉。 缺点: 项目的需求设计难以更改,各阶段的划分完全固定,产生大量文档。 客户在看到软件成品之前可能不知道他们的需求是什么,因此改变他们的需求,导致重新设计,重新开发和重新测试,并增加成本。 在设计新的软件产品或功能时,设计人员仅仅依据文档,可能不会意识到未来的困难,在这种情况下,存在错误预估人力分配等问题。 增量模型 增量模型是把待开发的软件系统模块化,将每个模块作为一个增量组件,从而分批次地分析、设计、编码和测试这些增量组件。运用增量模型的软件开发过程是递增式的过程。 Incremental_model 优点: 将待开发的软件系统模块化,可以分批次地提交软件产品,使用户可以及时了解软件项目的进展。 以组件为单位进行开发降低了软件开发的风险。一个开发周期内的错误不会影响到整个软件系统。 比其他软件开发方法更容易测试和调试,因为在每次迭代期间进行相对较小的更改。 允许部分利用产品并避免长的开发时间。 缺点: 由此产生的成本可能超过组织的成本。 要求待开发的软件系统可以被模块化。 随着产品中添加了额外的功能,可能会出现与系统架构相关的问题,这些问题在早期的原型中并不明显 螺旋模型 螺旋模型是风险驱动的软件开发过程模型,兼顾了快速原型的迭代的特征以及瀑布模型的系统化与严格监控,在每个迭代阶段构建原型是螺旋模型用以减小风险的途径。 spiral_model 优点: 适合大型软件的开发,对于需求不明确情况下,便于风险控制和需求变更。 能够让客户参与到项目每个阶段中,保证项目的方向不偏移。 能够进行风险的评估,从而避免了不合理的需求导致难以实现 每次循环都能得到一个软件原型来进行评估和客户的评价,提出修正建议 缺点: 开发周期长,客户未必接受长时间的开发和不稳定的结果。 风险分析需要相当丰富的评估经验,风险规避也需要深厚的专业知识。 迭代次数不确定,不适合大团队,无法确定发布日期。 简述统一过程三大特点,与面向对象的方法有什么关系? 迭代和增量的 统一过程是一个迭代和增量开发过程。精化,构建和过渡阶段分为一系列时间盒迭代。 (初始阶段也可以划分为大型项目的迭代。)每次迭代都会产生一个增量,这是系统的一个版本,它包含与之前版本相比增加或改进的功能。 以架构为中心 统一流程坚持认为架构是项目团队塑造系统的核心。由于没有一个模型足以涵盖系统的所有方面,因此统一过程支持多个体系结构模型和视图。 注重风险 统一过程要求项目团队专注于在项目生命周期的早期解决最关键的风险。必须选择每次迭代的可交付成果,特别是在精化阶段,以确保首先解决最大的风险。 关系:统一过程是面向对象的基于web的程序开发方法论。 简述统一过程四个阶段的划分准则是什么?每个阶段关键的里程碑是什么? 按时间将生命周期过程展开成四个阶段。 初始阶段:生命周期目标里程碑。制定系统的近似愿景,制定业务案例,确定范围,并对成本和进度进行粗略估算。 细化阶段:生命周期体系结构里程碑。解决已知的风险因素并建立和验证系统架构。 构造阶段:初始运行能力里程碑。最终的构建阶段可交付成果是可以在移交阶段部署的软件。 移交阶段:产品发布里程碑。在此阶段,系统将部署到目标用户,过渡阶段还包括系统转换和用户培训。 软件企业为什么能按固定节奏生产、固定周期发布软件产品?它给企业项目管理带来哪些好处? 因为软件企业采用了RUP,即统一过程,这样企业在在软件开发的过程中能够按照这四个阶段来进行开发,合理规划软件范围,有节奏地生产,发布,同时企业内部也有自身期限要求以及评估等等,能够指导开发人员在一定时间内完成任务,使企业能够固定周期地发布软件产品。 企业有节奏生产软件产品能增强企业在市场上的竞争力,能够使得资金得以进行周转,本身的稳定开发能够吸引更多的客户,有利于项目的及时提交从而获得反馈,降低了延期带来的风险,能够保证项目顺利完成。","tags":[{"name":"SSAD","slug":"SSAD","permalink":"https://zhangziquan.github.io/tags/SSAD/"}]},{"title":"软件测试(二)","date":"2019-03-30T11:39:00.000Z","path":"2019/03/30/软件测试(二)/","text":"软件测试(二) 题目要求:在敏捷开发方法的12条原则中挑选1条你感兴趣的原则进行风险评估。(参见Lec.5, slide 9 and Lec.3, slide 10) 常见风险因素 大型软件项目的风险管理:大型项目存在诸多风险因素,在不 同程度上对软件开发过程和软件产品质量造成影响。风险不能 全部消除,而只能采用避免、减轻、和接受三种应对策略。 需求变更风险; 进度风险、预算风险、管理能力风险、信息安全风险; 应用技术风险、质量控制风险、软件设计与开发工具风险、员工技能风险; 人力资源风险、政策风险、市场风险、营销风险 风险评估 选择对第二条进行风险评估 欣然面对需求变化,即使在开发后期也一样。为了客户的竞争优势,敏捷过程掌控变化。 需求变更风险:在需求变化后,存在很多需要解决的问题,导致程序开发过程中的成本增加,后期修改过程质量可能不过关,开发时期延长等等,特别是在开发后期,存在推倒重建风险。往往许多不清晰的客户需求会给开发带来灾难性的后果。所以提出的一些不合理的需求更改应当拒绝或者砍掉。 进度风险:需求的变化会使得开发进度缓慢,拖慢进度,使后期的任务难以完成或是无法在限定的期限完成,造成损失。 预算风险:和进度风险一样,抛弃旧的方案采用新方案则需要更多的预算,因此预估资金将会提高,需要重新考虑是否更改需求,否则会出现预算超支,或是为了满足预算而导致软件质量不过关。 管理能力风险:当客户需求发生变更的时候,项目开发可能因为没有能力去应对需求变更,在开发过程中对人力,组织的管理会出现混乱,打乱了正常的项目流程,而导致项目出现问题。 信息安全风险:当软件需求变更时,已完成的接口等的更改可能会并不完全被改掉,有可能会存在冗余的接口或者功能,之后的开发并未意识到其中的漏洞而被利用,导致信息安全问题。 应用技术风险:变更需求后可能需要额外的应用技术,若无相关知识人才,可能会增加人力预算或是学习成本,导致项目进度缓慢。 质量控制风险:需求变更导致预算或是进度方面出现问题,而选择加快进度,忽略了软件质量的把控,最终出现一个半成品或者是劣质品。 软件设计与开发工具风险:与应用技术风险相似,当需要额外的开发工具时,会增加开发成本(软件的费用)以及学习成本(时间)。 员工技能风险:和之前所说的学习成本一样,当需求变更可能需要相关技能的人员,若招不到相关人员采取临时培训的方法,会增加人力成本,导致项目进度缓慢,临时培训的员工技术不熟悉导致的质量问题。 人力资源风险:当需求变更后,项目在原有的基础上可能需要更多的人力去完成,出现人力资源问题。 市场风险:客户方提出的需求若是不清晰,模糊的需求,抑或是没有进行过市场调研的需求,会导致原本较好的方案被舍弃,而新的需求又没有较好的市场竞争力,导致项目失败。 营销风险:与市场风险类似,新的需求没有很好的市场竞争力,旧需求的基础上变更应当进行逐步更改,避免营销风险。","tags":[{"name":"software development","slug":"software-development","permalink":"https://zhangziquan.github.io/tags/software-development/"}]},{"title":"在hexo博客中添加视频","date":"2019-03-30T08:25:00.000Z","path":"2019/03/30/在hexo博客中添加视频/","text":"在hexo博客中添加视频 使用外链添加 代码如下:不作解释 1234567{% raw %}<div align=\"center\" style=\"position: relative; width: 700px; height: 508px; margin:0 auto\"> <iframe src=\"https://player.bilibili.com/player.html?aid=36432934&cid=63965248&page=1\" scrolling=\"no\" border=\"0\" frameborder=\"no\" framespacing=\"0\" allowfullscreen=\"true\" style=\"position:absolute; width: 100%; height: 100%; left: 0; top: 0%;\"> </iframe></div>{% endraw %}","tags":[{"name":"Hexo","slug":"Hexo","permalink":"https://zhangziquan.github.io/tags/Hexo/"}]},{"title":"系统分析与设计(二)","date":"2019-03-30T07:35:00.000Z","path":"2019/03/30/系统分析与设计(二)/","text":"系统分析与设计(二) 1、简答题 用简短的语言给出对分析、设计的理解。 分析:强调对问题和要求的调查,而不是解决方案。 设计:强调满足要求的概念解决方案(在软件和硬件中),而不是其实现。 用一句话描述面向对象的分析与设计的优势。 作为分析者不需要有很多问题域的专业知识,只需找出描述问题域及系统责任所需的对象。负责分析和实现的人可以使用通用的符号模型来进行交流。 简述 UML(统一建模语言)的作用。考试考哪些图? UML是统一建模语言,旨在提供一种可视化系统设计的标准方法。 作用是可以将分析结果和设计视觉化,简单又相对详细,可以用作简图、蓝图和编程语言。用来把现实中的问题抽象成面向对象的解决方案,以便进一步的编码。 考试: 用例图 用户角度 静态图 类图 对象图 包图 行为图 交互图 状态图 活动图 实现图 构件图 部署图 从软件本质的角度,解释软件范围(需求)控制的可行性 由于软件本身的复杂性、不可见性、不一致性、可变性,软件范围多数情况下对于客户和开发者都是模糊的,因此需要进行软件需求的控制来避免项目陷入混乱以及控制软件的复杂性。 所以通过软件范围(需求)控制对客户的各种需求进行可行性分析求精和细化,分析各种实现方式,从而进行软件各部分的设计。砍去一些客户不清晰的业务能够有效开发。 2、项目管理实践 看板使用练习 kanban UML绘图工具练习(提交贴图,必须使用 UMLet) UMLet 教材247页 教材247页","tags":[{"name":"SSAD","slug":"SSAD","permalink":"https://zhangziquan.github.io/tags/SSAD/"}]},{"title":"系统分析与设计(一)","date":"2019-03-15T09:08:00.000Z","path":"2019/03/15/系统分析与设计(一)/","text":"系统分析与设计(一) 软件工程的定义 在软件的开发、运行和维护中应用的一种系统的、可量化的方法,同时包含了对这一方法的研究,与软件生产的所有方面相关的工程学科。 解释导致 software crisis 本质原因、表现,述说克服软件危机的方法 名词解释 落后的软件生产方式无法满足迅速增长的计算机软件需求,从而导致软件开发与维护过程中出现一系列严重问题的现象。 本质原因 硬件的快速发展导致计算能力的极大提高,超过了开发软件有效利用这些计算性能的能力。 软件的复杂度不断增加,但由于现有方法不足,对大型、复杂软件存在无法解决问题,即软件开发方法没有跟上越来越复杂的软件开发。 具体表现 项目运行超过预算 项目运行超时 软件的使用效率较低 软件质量过差 软件不能满足用户需求 项目无法管理,代码难以维护 软件无法交付 克服方法 因为面临的不仅仅是技术问题,还有管理问题,所以可以从应用现代工程的概念、原型、技术、方法进行计算机软件的开发、管理、和维护。如使用一些优秀的代码管理软件等等。 软件工程从硬件工程和其他人类工程中吸收成功的经验,明确提出了软件生命周期的模型,发展软件开发与维护阶段适用的技术和方法,并应用于软件工程实践。 软件生命周期 在软件工程中,软件开发过程是将软件开发工作划分为不同阶段以改进设计,产品管理和项目管理的过程。软件生命周期是软件的产生直到报废的生命周期,周期内有问题定义、可行性分析、总体描述、系统设计、编码、调试和测试、验收与运行、维护升级到废弃等阶段,常见有瀑布模型、螺旋模型、敏捷的模型等。 SWEBoK 的 15 个知识域 软件需求 关注软件需求的启发,协商,分析,规范和验证。软件需求表达了对软件产品的需求和限制,这些需求和约束有助于解决一些现实问题。 软件设计 设计被定义为定义系统或组件的体系结构,组件,接口和其他特征的过程以及过程的结果。软件设计涵盖了设计过程和最终产品。软件设计过程是软件工程生命周期活动,其中分析软件需求以产生软件内部结构及其行为的描述,其将作为其构造的基础。软件设计(结果)必须描述软件体系结构 - 即软件如何分解和组织成组件以及这些组件之间的接口。它还必须描述能够构建它们的详细程度的组件。 软件构建 软件构建是指通过结合详细设计,编码,单元测试,集成测试,调试和验证来详细创建工作软件。软件构建包括与满足其要求和设计约束的软件程序开发相关的主题。软件测试涵盖了软件构建基础;管理软件建设;建筑技术;实际考虑;和软件构建工具。 软件测试 测试是一项旨在评估产品质量并通过识别缺陷来改进产品质量的活动。软件测试涉及在有限的测试用例集上针对预期行为动态验证程序的行为。这些测试用例是从(通常非常大的)执行域中选择的。软件测试包括软件测试的基础知识;测试技术;人机界面测试与评估;与测试有关的措施;和实际考虑。 软件维护 软件维护包括增强现有功能,调整软件以在新的和修改的操作环境中运行,以及纠正缺陷。这些类别称为完善,自适应和纠正性软件维护。软件维护包括软件维护的基础知识(维护的性质和需求,维护类别,维护成本);软件维护中的关键问题(技术问题,管理问题,维护成本估算,软件维护测量);维护过程;软件维护技术(程序理解,重新设计,逆向工程,重构,软件退役);灾难恢复技术和软件维护工具。 软件配置管理 软件配置管理(SCM)是在不同时间点识别系统配置的规则,用于系统地控制配置的改变,以及在整个软件生命周期中维持配置的完整性和可追溯性。软件配置管理涵盖SCM过程的管理;软件配置识别,控制,状态核算,审计;软件发布管理和交付;和软件配置管理工具。 软件工程管理 软件工程管理涉及规划,协调,测量,报告和控制项目或程序,以确保软件的开发和维护是系统化的,规范化的和量化的。软件工程管理涵盖了启动和范围定义(确定和协商要求,可行性分析以及要求的审查和修订);软件项目计划(过程计划,工作量估算,成本和进度,资源分配,风险分析,质量计划);软件项目制定(计量,报告和控制;收购和供应商合同管理);产品验收;审查和分析项目绩效;项目结束;和软件管理工具。 软件工程过程 软件工程过程关注软件生命周期过程的定义,实施,评估,测量,管理和改进。涵盖的主题包括流程实施和变更(流程基础架构,流程实施和变更模型以及软件流程管理);流程定义(软件生命周期模型和流程,流程定义,流程适应和流程自动化的符号);过程评估模型和方法;测量(过程测量,产品测量,测量技术和测量结果的质量);和软件处理工具。 软件工程模型和方法 软件工程模型和方法解决了涵盖多个生命周期阶段的方法;其他涵盖特定生命周期阶段的特定方法。涵盖的主题包括建模(软件工程模型的原理和属性;语法与语义与不变量;前置条件,后置条件和不变量);模型类型(信息,结构和行为模型);分析(分析正确性,完整性,一致性,质量和相互作用;可追溯性;以及权衡分析);和软件开发方法(启发式方法,形式方法,原型方法和敏捷方法)。 软件质量 软件质量是许多SWEBOK V3 KAs中普遍存在的软件生命周期问题。此外,软件质量KA还包括软件质量的基础知识(软件工程文化,软件质量特性,软件质量的价值和成本以及软件质量改进);软件质量管理流程(软件质量保证,验证和确认,审核和审核);和实际考虑(缺陷表征,软件质量测量和软件质量工具)。 软件工程专业实践 软件工程专业实践关注软件工程师必须具备的专业,负责和道德的软件工程知识,技能和态度。软件工程专业实践涵盖专业性(专业行为,专业协会,软件工程标准,雇佣合同和法律问题);道德准则;小组动态(团队合作,认知问题复杂性,与利益相关者互动,处理不确定性和模糊性,处理多元文化环境);和沟通技巧。 软件工程经济学 软件工程经济学关注的是在业务环境中做出决策,以使技术决策与组织的业务目标保持一致。涵盖的主题包括软件工程经济学的基本原理(提案,现金流量,货币时间价值,计划视野,通货膨胀,折旧,替代和退休决策);非营利性决策(成本效益分析,优化分析);估计,经济风险和不确定性(估算技术,风险决策和不确定性);和多属性决策(价值和衡量尺度,补偿和非补偿技术)。 计算基础 计算基础涵盖了提供软件工程实践所需的计算背景的基础主题。涵盖的主题包括问题解决技术,抽象,算法和复杂性,编程基础,并行和分布式计算的基础知识,计算机组织,操作系统和网络通信。 数学基础 数学基础涵盖了提供软件工程实践所必需的数学背景的基础主题。涵盖的主题包括集合,关系和功能;基本命题和谓词逻辑;证明技术;图形和树木;离散概率;语法和有限状态机;和数论。 工程基础 工程基础涵盖了提供软件工程实践所必需的工程背景的基础主题。涵盖的主题包括经验方法和实验技术;统计分析;测量和指标;工程设计;仿真与建模;和根本原因分析。 简单解释 CMMI 的五个级别 Maturity Level 1 - Initial 初始级 软件工程是无序的,自发生产模式,对过程没有定义,管理是反应式的 Maturity Level 2 - Managed 可管理级 建立了基本的项目管理过程来跟踪费用、进度和功能特性。制定了必要的过程纪律,能重复早先类似应用项目取得的成功经验。 Maturity Level 3 - Defined 已定义级 已将软件管理和工程两方面的过程文档化、标准化,并综合成该组织的标准软件过程。所有项目均使用经批准、剪裁的标准软件过程来开发和维护软件,软件产品的生产在整个软件过程是可见的。 Maturity Level 4 - Quantitatively Managed 量化管理级 分析对软件过程和产品质量的详细度量数据,对软件过程和产品都有定量的理解与控制。管理有一个作出结论的客观依据,管理能够在定量的范围内预测性能。 Maturity Level 5 - Optimizing 优化管理级 过程的量化反馈和先进的新思想、新技术促使过程持续不断改进。 用自己语言简述 SWEBok 或 CMMI SWEBok 即软件工程知识体系指南,描述现在普遍接受的一些关于软件工程方面的知识,促进全球关于软件工程观点的统一,因为现在的硬件发展速度加快,为了加强软件开发的技术,克服软件危机,所以IEEE Computer Society 构建了这个软件生产的最佳实践与相关知识的框架,用来指导软件工程的人才培养和学科建设。 SWEBok 也在随着实践发展不断变化,收录广泛接受的实践知识,根据其它知识体系不同观点,进行重新修订。 CMMI 即软件能力成熟度模型,它的目的是帮助软件企业对软件工程过程进行管理和改进,增强开发与改进能力,从而能按时地、不超预算地开发出高质量的软件。也可以用作来评估组织过程成熟度的框架。CMMI提供一个单一的集成化框架,来消除组织在各个过程中模型的不一致,减少重复,试图通过管理的实践和过程改进来克服软件开发的困难。","tags":[{"name":"SSAD","slug":"SSAD","permalink":"https://zhangziquan.github.io/tags/SSAD/"}]},{"title":"Smart-Market","date":"2018-12-31T11:37:00.000Z","path":"2018/12/31/Smart-Market/","text":"区块链最终报告 Github:https://github.com/zhangziquan/Smart-Market Blog:https://www.ziquanzhang.ink/ 建议还是打开github或者blog查看markdown格式的报告,转成pdf大小会很奇怪。。 选题背景 现在很多地方流行租赁,二手转卖,但是很难保证能够做到货真价实,常常会发生一些意外,比如一些明明是三四手的东西却标明是全新,二手,95成新等等。这些不对称信息使得买家会陷入劣势的境地。另外也有一些信用不好的买家,往往到手后挑刺砍价,简称“手刀”,既不点确认收货,也不退回货,让卖家陷入钱不到,货也没有的两难境地,只能接受砍价。 还有一些则是虚拟货品的发货,比如steam的key,某些激活码,优惠券,dota饰品,apple充值卡,点卡以及购物卡等等,虽然有rep声誉系统来给予判断信誉,但是难免会有一些恶意分子伪造造假,让一些新手被骗。 另外由于一些骗子的存在让很多人排斥个人交易,寻求第三方平台又往往会付出较多的手续费,并且流程非常的繁琐。所以需要一个双方都认可的,又可以相互信任的平台来进行交易。 选题依据 因此我们可以利用区块链来做一个二手平台,卖家将货品的信息上传到区块链中,之后这件物品的交易数据和走向都会被记录在区块链上,这样可以方便买家查询所要购买的东西的信息,为了防止卖家上传虚假信息,将会查询卖家之前所购买的二手商品,进行查重验证,避免信息不符。在交易完成之后,若发生到手后砍价等恶意行为,卖家可以进行评价,加入到个人信誉信息之中,这样能够提供给别的卖家进行谨慎的选择是否出售。 特别适用于一些虚拟货品之类的东西,可以直接部署智能合约自动执行转账发货等功能,当收到转账后自动执行发货,这就避免了恶意分子收钱不发货等,实现点对点交易,凭借不可篡改的技术得到真实信息,提高交易效率。另外因为信息发布到链上使得查询就变得非常方便,有利于买家能够进行挑选。 买家和卖家提供短时间内的密钥用于查询对方的交易历史是否正常。 在链上部署智能机器人能够进行回答各种问题,负责解决某些常见的问题回答。 使用说明 部署说明: !!先使用npm install 安装所需要的依赖项!! 1npm install 启动私链,rpc端口为8454,解锁账户 安装truffle,进行智能合约的编译(因为使用了新特性,所以出现了warning) 编译完智能合约之后,进行智能合约的部署 进行挖矿,使部署完成 然后就可以启动DAPP了,即区块链应用 start 最终产品界面: change 用户界面: client 用户界面用于客户进行浏览,购物使用,可以查看商品的价格,状态,是否有人竞拍等等,方便客户进行浏览和选择。 Input Usage Address 输入客户用于购买账户的地址 Password 测试时可以不要,应用时需要解锁账户,因此需要密码,也可以另外解锁之后再使用。 No 可以快速根据货物id进行查找,也可以在右边的下拉栏进行拉去查看。 Value 输入您需要付出的价钱,因为这里有个拍卖系统以及一口价系统,当价钱超过一口价时自动获得。否则进入拍卖。所以要注意查看当前的商品价钱。当价钱不足时自动退回,但要扣除一些手续费。 管理界面: server Server 而管理界面则方便合约拥有者进行合约的管理,如管理商品,上架和下架,确认订单等等。查看各个货物的状态,当前价格,是否有人拍下等等。订单管理则是根据当前商品信息来获得,可以选择确认订单。 Input Usage Address 输入管理者的创建合约的地址用于验证信息 Password 测试时可以不要,应用时需要解锁账户,因此需要密码,也可以另外解锁之后再使用。 Name 输入上架货物的名称,可以自行定义,方面客户进行浏览确认 Price 输入上架货物的价格,为了方便管理,当前输入的价格为竞拍低价,一口价为低价2 Key 即输入货物的关键使用密钥,激活码等等,存储在合约内,仅当货物在完成或出货的时候,可以由客户取得 右边的浏览则是浏览整个商品列表,可以方便进行所有商品的浏览查看。 测试 利用testrpc进行DAPP的测试,因为不需要解锁和挖矿,因此十分地方便。 客户端: create 进行交易的创建,创建了几个商品,通过填写各种信息,点击创建按钮即可创建。 query 此时可以在客户端进行查看,通过下拉栏来查看全部货物的情况,可以显示id,名称,价格,以及当前的状态。 buy 输入了客户的信息,解锁了账户之后,填入价格即可进行购买的操作,弹窗显示购买成功。 statechange 因为是一口价价格,所以状态变成已锁定,即该商品已被某个买家确定了,此时其他买家是不可以进行购买的。然后买家就可以等待卖家进行发货,即确认订单,待确认订单之后,即可取货。 confirm 卖家通过订单管理,查看筛选出所有被锁定的订单,然后即可进行确认的操作,也可以取消订单,但要付出一定的代价(想法是根据市场价波动来确定代价)。确定之后商品的状态则被改变。 statechange 在客户端进行查看即可查看到刚刚购买的商品的状态从“已锁定”变为了“待取货”,提示客户已经可以取货了,这个时候就可以进行取货操作,输入刚刚用于购买的账户地址,点击pickup按钮。 extract 取货成功,获得key即虚拟商品的关键信息,即可进行使用,这个时候商品的状态则会变成“已完成”,其它买家同样也不能进行购买。这个会一直保持显示一段时间,之后从下拉栏中移除,若有需要还是可以通过No输入id来进行订单的查看,同样可以取得key。这是为了避免完成的订单妨碍到其它客户的浏览。 extract 测试从未锁定的订单来进行取货,可以看到没有权限获得key。 extract 测试从未确定的订单来进行取货,可以看到没有权限获得key。同样如果输入的地址不是当初用于转账的地址,同样也不能进行取货。 info 智能合约的拥有者可以在后台查看当前合约拥有的代代币,以及正在出售的商品,拥有者可以在客户评价之后取出已售出的商品的(押金+售出金额),未售出的就不能进行取出,同样若买家长时间为评价则取消其评价资格,默认好评。 change 进行页面的切换,显示正常。 待改进 还有很多地方尚待完善,比如评价系统,竞拍系统,以及客户主动取消订单的系统,已经划分好了tab选项卡,进行完善,之后的主页则会是登陆一样的界面,输入地址密码后自动锁定,后面的操作就不需要再次输入了。 机器人的查询等等可以使用JS来进行实现。。。比较来说Solidity真的是十分的蛋疼,而且还在不断更新,过了一个版本之后很多东西就变了。所以考虑使用JS做机器人的各种设置,再调用合约函数来实现。https://www.ziquanzhang.ink/根据之前的博客设计在看板娘的基础上加上点击等功能就差不多了。。。 实验心得 这次实验使用了layui的模板。。有点麻烦,因为这有很多动态生成的东西,改变了html之后又要重新渲染一遍,不然无法正确显示。特别是truffle的js有点奇怪,不知道怎么去手动添加一个js,只好把一些代码写到了html里,然后html里有layui和jquery的连接,通过这种方式来进行调用。这次实验还复习了一下javaScript和前端的设计。 有点不好的地方就是智能合约的调用函数的时候,如果发送交易的方式调用,返回的东西都接受不了,好像是要通过一个写在合约内的监视器来进行异步调用返回结果。但是我选择了每次调用之后用call调用来获取合约内的信息进行页面更新。","tags":[{"name":"ethereum","slug":"ethereum","permalink":"https://zhangziquan.github.io/tags/ethereum/"}]},{"title":"2018算法设计期末项目","date":"2018-12-23T17:27:00.000Z","path":"2018/12/24/2018算法设计期末项目/","text":"2018算法设计期中项目 题目描述 Suppose there are n facilities and m customers. We wish to choose: (1) which of the n facilities to open (2) the assignment of customers to facilities The objective is to minimize the sum of the opening cost and the assignment cost. The total demand assigned to a facility must not exceed its capacity. Example: upload successful 题目分析 这道题目的意思是有多个工厂和消费者,要找到一个组合,使得能够服务所有的客户,并且消耗最少,约束为工厂有一定的容量,一个工厂不能容纳过多的客户,而且客户到不同工厂的消耗也是不一样的。因此这个题目需要我们找到最优近似解。 解题思路 这道题我想的是既然是一个NPhard的问题,那么就使用搜索去寻找接近最优的解,而这和最近做的TSP问题十分地相似,TSP问题是寻找最短的路径,那么也可以用到这一个题目上,只要弄好数据结构,即解的表示方法,就可以使用几种搜索方法:邻域搜索,模拟退火,遗传算法。 初始解可以随机也可以使用贪心策略来进行初始化解。 数据结构 已知有工厂,客户这两个类型,所以做成一个解,把客户的消耗表放在工厂这个类型里,因为给出的数据集是每一个工厂对应的客户的消耗。 工厂类存储对应服务的客户id以及他们的消耗表。客户类仅需存储demand判断是否超过工厂容量即可,为了之后的方便查询,也存放工厂的id,便于最后的输出。 upload successful 1234567891011121314public class facility { int id = 0; int opencost = 0; int capacity = 0; int allcost = 0; Vector<Integer> clients; Vector<Integer> cost;···public class customer { int id = 0; int demand = 0; int fid = -1;··· 模拟退火 算法介绍 模拟退火是邻域搜索的一种改进的方法,在邻域搜索上加上了温度这一元素,两重循环,这里我用的是外层温度下降,内层达到热平衡的循环,内层循环进行邻域解的搜索,在搜索到差解的时候,也有一定概率会接受,但是随着温度逐渐下降,接受差解的概率也会逐渐减少,最后收敛。 模拟退火的主要作用就是跳出局部最小值,因为达到局部最小时,因为步幅过小,导致无法跳出局部解,最后使得只能达到局部最优。所以要跳出局部最优,只能接受差解,到达另一个局部最优,这样逐步能接近全局最优,并且在最后接受差解概率变低,使得能够达到最优的解。 算法代码 1234567891011121314151617181920212223242526272829303132333435public void search() { times *= 1.01; int t = times; while (t != 0) { Random rand = new Random(); int fpos1 = rand.nextInt(fnum); int fpos2 = rand.nextInt(fnum); if (fpos1 == fpos2 || !facilities[fpos1].isOpened()) { continue; } facility facility1 = facilities[fpos1]; facility facility2 = facilities[fpos2]; Vector<Integer>client1 = facility1.getClient(); int cpos1 = rand.nextInt(client1.size()); int cid = client1.get(cpos1); customer customer1 = customers[client1.get(cpos1)]; double newcost = getCost(); if(facility2.getCapacity() > customer1.getDemand()) { newcost -= facility1.getCostTo(customer1.getId()); newcost += facility2.getCostTo(customer1.getId()); }else { continue; } double offset = newcost - getCost(); if (offset <= 0 || Math.pow(E, -offset * 1.0 / temperature) >= Math.random()) { facilities[fpos1].removeClient(customer1); facilities[fpos2].addClient(customer1); customers[cid].setfid(fpos2); } t--; cost = getCost(); }} 遗传算法 算法介绍 遗传算法是利用种群的优势,让优势解能够将基因遗传下来,得到更好的解,发挥种群的优胜劣汰的准则,同时因为交叉得到的解有更好的效果。到后期则需要一些变异来防止其收敛过慢,增大扰动,促进算法的进行。 遗传算法主要是表示种群里的个体的基因,对每一个个体进行评价,然后采用轮盘法来筛选交配的父代,由父代基因交换来生成子代,这里使用的是交叉基因,采用基因映射的方法来解决冲突,最后再给子代进行突变,这样就能形成新的解。 初始解:一般建议先使用贪心来获得初始。 循环迭代: 评价:评价个体确定适应值 选择:根据适应积累值,采用多种方法选择作为交配的父代 交配:有概率地交换基因片段,产生子代。 变异:子代有一定概率发生变异。 基因编码 12345678910111213141516class Genome{public: friend class GenAlg; friend class GenEngine; Genome():fitness(0){} Genome(vector <double> vec, double f): vecGenome(vec), fitness(f){} //初始化参数。private: vector <facility[]> vecGenome; // 装载基因的容器 double fitness; //适应度 }; 算法代码 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155public void GASearch() { int generation = 0; init(); while(generation < maxgen) { assess(); select(); crossover(); mutate(); generation ++; population.removeAllElements(); population.addAll(newpopulation); System.out.println(\"此时最佳消耗为:\" + getCost(population.get(bestsolve)) + \"适应度为 \" + bestfitness); }} //选择交配个体private void select() { newpopulation.removeAllElements(); for(int i = 0; i < population.size(); i++) { Random rand = new Random(); double p = rand.nextInt(1000)/1000.0; if(p<liveper[0]) { int[] temp = new int[count]; copyArray(temp,population.get(0)); newpopulation.add(temp); }else { for(int j = 0; j < population.size(); j++) { if(p >= liveper[j] && p < liveper[j+1]) { int[] temp = new int[count]; copyArray(temp,population.get(j+1)); newpopulation.add(temp); } } } }} //进行交配private void crossover() { int first = 0; // 被选择的个数。 int one = 0; for(int i = 0; i< newpopulation.size(); i++) { Random rand = new Random(); double p = rand.nextInt(1000)/1000.0; if(p < PXOVER) { first++; if(first%2 == 0) { Xover(one,i); }else { one = i; } } }}//进行染色体互换private void Xover(int one, int two) { Random rand = new Random(); int point1 = rand.nextInt(count); int point2 = rand.nextInt(count); //选择交换点 if(point1>point2) { int temp = point1; point1 = point2; point2 = temp; } int[] child1 = new int[count]; int[] child2 = new int[count]; copyArray(child1, newpopulation.get(one)); copyArray(child2, newpopulation.get(two)); //交换基因片段 for(int i = point1; i < point2; i++) { child1[i] = newpopulation.get(two)[i]; child2[i] = newpopulation.get(one)[i]; } //解决子代冲突 for(int i = 0; i< point1;i++) { //替换冲突的为父代对应位置的基因 for(int j = point1; j < point2; j++) { if(child1[i] == child1[j]) { child1[i] = newpopulation.get(one)[j]; j = point1 - 1; } } } for(int i = 0; i< point1;i++) { //替换冲突的为父代对应位置的基因 for(int j = point1; j < point2; j++) { if(child2[i] == child2[j]) { child2[i] = newpopulation.get(two)[j]; j = point1 - 1; } } } for(int i = point2; i<count;i++) { for(int j = point1; j < point2; j++) { if(child1[i] == child1[j]) { child1[i] = newpopulation.get(one)[j]; j = point1 - 1; } } } for(int i = point2; i<count;i++) { for(int j = point1; j < point2; j++) { if(child2[i] == child2[j]) { child2[i] = newpopulation.get(two)[j]; j = point1 - 1; } } } newpopulation.remove(one); newpopulation.insertElementAt(child1, one); newpopulation.remove(two); newpopulation.insertElementAt(child2, two);}//子代发生变异private void mutate() { for(int i = 0; i < newpopulation.size(); i++) { Random rand = new Random(); double p = rand.nextInt(1000)/1000.0; if(p < PMUTATION) { int pos1 = rand.nextInt(count); int pos2 = rand.nextInt(count); if (pos1 > pos2) { int temp = pos1; pos1 = pos2; pos2 = temp; } if (pos1 == pos2 || (pos1 == 0 && pos2 == count - 1)) { continue; } while (pos1 < pos2) { int temPoint = newpopulation.get(i)[pos1]; newpopulation.get(i)[pos1] = newpopulation.get(i)[pos2]; newpopulation.get(i)[pos2] = temPoint; pos1++; pos2--; } } }} Result table Result Time(ms) p1 9145.0 220ms p2 7942.0 104ms p3 9913.0 150ms p4 11526.0 156ms p5 9173.0 140ms p6 7855.0 161ms p7 9577.0 123ms p8 11899.0 120ms p9 8976.0 130ms p10 7726.0 120ms p11 9703.0 76ms p12 11391.0 41ms p13 10747.0 163ms p14 8163.0 297ms p15 11277.0 130ms p16 13855.0 169ms p17 9896.0 230ms p18 8250.0 240ms p19 11337.0 250ms p20 13175.0 261ms p21 9876.0 193ms p22 8181.0 286ms p23 10599.0 221ms p24 13547.0 89ms p25 12679.0 1550ms p26 11534.0 130ms p27 13373.0 360ms p28 15498.0 244ms p29 13957.0 1227ms p30 12166.0 378ms p31 14788.0 1485ms p32 18028.0 117ms p33 12721.0 1509ms p34 11424.0 313ms p35 13901.0 241ms p36 16488.0 710ms p37 12212.0 1347ms p38 11418.0 269ms p39 13515.0 564ms p40 15230.0 346ms p41 7104.0 104ms p42 8150.0 196ms p43 9158.0 374ms p44 7154.0 110ms p45 8567.0 210ms p46 9275.0 348ms p47 6312.0 49ms p48 7307.0 173ms p49 8632.0 356ms p50 9109.0 124ms p51 8683.0 457ms p52 9290.0 93ms p53 9512.0 539ms p54 9227.0 171ms p55 9005.0 446ms p56 23882.0 130ms p57 32882.0 244ms p58 52762.0 106ms p59 39121.0 30ms p60 23882.0 140ms p61 32882.0 144ms p62 53321.0 86ms p63 39121.0 230ms p64 23882.0 244ms p65 32882.0 180ms p66 52949.0 180ms p68 23882.0 178ms p69 32882.0 162ms p70 53203.0 286ms p71 38648.0 235ms Detailed solution 9119.0 1 1 1 1 1 1 1 0 1 1 8 2 1 6 3 8 2 4 4 1 9 0 3 2 8 3 4 0 9 4 3 4 6 4 2 6 1 5 0 5 2 6 0 3 4 4 4 3 0 4 3 8 1 5 4 0 9 0 2 0 7957.0 1 1 1 1 1 1 1 1 1 1 8 2 1 6 3 8 2 4 4 1 9 0 3 2 8 3 4 0 9 7 3 4 6 4 2 5 1 5 0 5 2 6 0 3 4 4 4 3 0 4 1 8 1 6 7 0 4 0 4 0 9897.0 1 1 1 1 1 0 1 1 1 1 8 2 1 6 3 8 2 4 4 1 9 8 3 2 0 3 4 0 9 7 3 4 6 4 2 6 1 6 0 1 6 6 0 3 4 4 4 3 0 4 2 8 1 6 7 0 4 0 4 0 12005.0 1 1 1 1 1 1 1 1 1 1 0 2 1 6 3 8 2 4 4 1 9 8 3 2 8 3 4 0 9 7 3 8 6 4 2 5 1 5 0 5 2 6 0 3 9 4 4 3 0 4 1 8 1 5 7 0 4 0 4 3 9175.0 1 1 1 1 1 1 1 1 1 1 8 8 1 6 1 8 2 4 4 1 9 8 0 2 8 3 4 0 9 7 3 8 6 4 2 5 1 5 0 5 2 6 0 3 9 4 4 3 0 4 1 8 1 5 7 0 2 0 7 3 7873.0 1 1 1 1 1 1 1 1 1 1 8 8 1 6 3 8 2 4 4 1 9 8 3 2 8 3 2 0 9 7 3 8 6 4 2 5 1 5 0 5 2 6 0 3 9 4 4 3 0 4 1 8 1 6 7 0 9 3 4 0 9899.0 1 1 1 1 1 1 1 1 1 1 8 2 1 6 3 8 2 4 4 1 9 8 3 2 8 3 4 0 9 7 3 8 6 4 2 5 1 5 0 5 2 6 0 3 9 4 4 3 8 4 1 8 1 5 7 0 9 0 7 0 11855.0 1 1 1 1 1 1 1 1 1 1 8 8 1 6 3 8 2 4 4 1 9 8 3 2 8 3 4 0 9 7 3 8 6 4 2 5 1 5 0 5 2 6 0 3 9 4 4 3 0 4 1 8 1 5 7 0 2 3 7 0 8928.0 1 1 1 1 1 0 1 1 1 1 8 8 1 6 3 8 2 4 4 1 9 8 0 2 8 3 2 0 9 7 3 8 6 4 2 6 1 1 0 1 2 6 0 3 9 4 4 3 0 4 3 8 1 6 7 0 2 0 4 0 7726.0 1 1 1 1 1 1 1 1 1 1 8 8 1 6 3 8 2 4 4 1 9 8 3 2 8 3 4 0 9 7 3 8 6 4 2 5 1 5 0 5 2 6 0 3 9 4 4 3 0 4 1 8 1 5 7 0 4 0 4 0 9726.0 1 1 1 1 1 1 1 1 1 1 8 8 1 6 3 8 2 4 4 1 9 8 3 2 8 3 4 0 9 7 3 8 6 4 2 5 1 5 0 5 2 6 0 3 9 4 4 3 0 4 1 8 1 5 7 0 4 0 4 0 11587.0 1 1 1 1 1 1 1 0 1 1 8 8 1 6 3 8 2 4 4 1 9 8 0 2 8 3 2 0 9 4 3 8 6 4 2 5 1 5 8 5 4 6 0 3 4 4 4 3 0 4 5 8 3 5 4 0 2 0 4 0 10350.0 1 1 0 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 1 16 12 10 10 12 15 17 13 12 17 7 19 19 10 13 3 15 10 17 9 12 15 15 19 9 17 0 17 1 17 13 7 13 16 3 10 11 19 19 13 9 0 0 7 8 13 14 10 19 9 8340.0 1 1 0 1 0 0 1 1 1 1 1 1 0 1 1 1 1 1 0 1 16 14 10 10 19 15 17 13 14 17 15 19 19 10 13 3 15 10 17 9 14 15 15 19 6 17 0 17 1 17 13 7 13 16 3 10 11 1 19 13 6 0 0 0 8 15 14 10 19 9 10865.0 1 0 1 1 1 0 1 0 0 1 1 0 0 1 1 1 1 1 0 1 16 3 10 10 19 13 17 2 14 17 15 19 19 6 13 3 15 10 4 9 14 15 15 19 6 17 16 17 17 17 13 14 2 16 3 10 17 19 19 13 6 0 15 15 9 13 14 10 19 9 13502.0 1 1 0 1 1 0 1 1 0 1 1 0 0 1 1 1 0 1 0 1 0 17 10 10 19 19 17 13 14 17 15 19 19 6 13 3 15 10 4 9 14 15 7 19 6 17 0 17 1 17 13 7 19 0 3 10 3 19 19 13 14 0 15 0 6 13 14 6 19 9 9598.0 1 1 0 1 1 0 1 0 0 1 1 0 0 1 1 1 1 1 0 1 16 1 10 10 1 15 17 13 14 17 15 4 1 6 13 3 15 10 4 9 14 15 15 19 6 1 16 17 1 17 13 15 13 16 3 10 3 19 19 13 10 0 0 0 3 15 14 10 19 9 8152.0 1 0 0 1 1 0 1 1 0 1 1 1 0 1 1 1 1 1 1 1 0 3 10 10 19 18 17 13 14 17 15 4 19 6 13 3 15 10 4 9 14 15 15 19 6 17 16 17 3 17 13 7 13 16 3 10 11 19 19 13 6 0 0 0 11 15 14 10 19 9 11267.0 1 1 0 1 1 0 1 1 1 1 1 0 1 1 0 1 0 1 0 1 0 12 10 10 1 15 17 13 12 17 15 19 1 6 13 3 15 10 4 9 12 15 7 19 6 17 0 17 3 17 13 7 13 0 3 10 3 19 19 19 9 0 0 7 8 13 12 10 19 9 13513.0 1 1 0 0 1 0 1 0 0 1 1 1 0 1 1 1 1 1 0 1 16 1 16 10 1 19 17 13 14 17 15 19 1 10 13 11 15 6 4 9 15 15 15 19 6 1 16 17 1 17 13 15 19 16 1 10 11 19 19 13 6 0 15 16 11 13 14 10 19 9 10423.0 1 1 0 1 1 0 1 0 1 1 1 0 0 1 1 1 1 1 0 1 16 1 10 10 1 13 17 13 14 17 14 4 1 10 13 3 15 10 4 9 14 15 15 19 10 17 16 17 1 17 13 14 13 0 3 10 3 19 19 13 6 0 0 16 8 0 14 10 4 9 8152.0 1 1 0 1 0 0 1 0 0 1 1 1 0 1 1 1 0 1 1 1 0 14 10 10 1 18 17 13 14 17 15 19 1 10 13 14 15 10 17 9 14 15 15 19 6 17 0 17 19 17 13 0 13 0 3 10 11 1 19 13 6 0 0 0 3 15 14 10 19 9 10946.0 1 1 0 1 0 0 1 0 0 0 1 1 0 1 1 1 1 1 1 1 0 3 10 10 1 18 17 13 14 17 15 19 1 10 13 3 15 10 17 6 14 15 15 19 6 17 16 17 3 17 13 15 13 16 19 10 11 19 19 13 6 0 0 0 6 15 14 6 19 6 13985.0 1 1 0 0 0 0 1 1 0 1 1 1 0 1 1 1 1 1 1 1 16 1 10 10 1 18 17 13 14 17 7 1 19 10 13 11 15 10 17 6 14 15 15 19 6 17 0 17 1 17 13 7 18 0 1 10 11 19 19 13 10 0 0 7 9 15 14 10 19 9 12567.0 1 0 1 0 0 1 0 0 1 0 0 1 0 0 1 1 0 1 0 0 1 0 0 0 1 1 0 0 0 0 20 11 20 14 20 5 2 24 14 17 24 20 8 20 0 14 14 17 5 11 2 0 24 0 24 8 2 24 5 24 5 25 20 25 20 20 24 15 11 14 0 14 15 8 24 14 11 20 11 8 11 14 14 5 20 20 17 11 11 14 0 5 20 11 11 11 24 0 5 24 20 20 11 0 24 8 24 20 14 24 20 0 20 17 2 17 17 0 20 5 2 24 17 11 0 5 2 14 0 8 8 0 17 0 2 25 17 11 14 8 14 14 2 11 11 25 24 15 5 24 5 8 25 20 20 11 5 24 0 5 0 11 24 25 20 14 0 5 20 17 0 8 20 14 8 25 25 5 2 5 11402.0 1 0 1 0 0 1 0 0 1 0 0 1 0 0 1 1 0 1 0 0 1 0 0 0 1 0 0 0 0 0 20 11 20 14 8 5 2 24 14 17 24 20 20 20 0 14 14 17 11 11 14 0 24 2 5 8 2 24 5 24 5 11 20 0 20 20 24 24 11 14 0 8 15 20 24 14 5 20 11 8 5 14 14 5 20 20 17 5 0 14 0 5 20 11 11 0 24 0 11 5 8 20 11 17 24 8 24 20 14 24 20 0 20 17 0 17 0 11 20 5 2 24 0 11 0 5 2 0 0 8 14 0 17 11 2 5 0 11 14 8 14 14 2 11 11 11 24 24 24 24 5 14 11 20 20 17 5 24 11 5 0 11 24 0 20 14 11 5 20 17 0 8 20 14 8 11 11 5 2 5 13410.0 1 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 0 1 0 0 0 0 0 20 11 20 14 20 5 0 24 11 17 5 14 20 20 2 14 17 0 5 11 14 17 24 0 24 20 2 24 24 24 11 11 20 5 20 20 24 24 0 14 0 8 24 20 24 14 0 20 0 14 5 14 14 5 20 20 14 17 17 14 0 5 20 11 11 11 24 0 5 24 20 20 5 0 24 20 24 20 0 24 20 11 20 17 0 17 2 0 20 11 2 24 0 11 17 5 2 11 11 8 20 2 17 11 2 11 2 11 8 14 14 14 2 11 11 5 24 24 5 24 5 20 11 20 20 11 5 24 0 11 0 17 24 5 8 14 0 5 20 17 0 8 20 14 8 0 0 5 2 5 16049.0 1 0 1 0 0 1 0 0 1 0 0 1 0 1 1 0 0 1 0 0 1 0 0 0 1 1 0 0 0 0 20 25 20 2 20 5 14 24 14 17 5 8 20 20 0 14 14 17 5 11 14 0 5 17 25 20 2 24 24 24 25 11 8 11 20 20 24 24 0 14 0 14 24 14 24 14 11 20 11 8 11 14 14 5 20 20 17 25 0 14 0 24 20 0 0 11 24 0 24 24 20 20 11 0 24 20 24 20 14 5 20 0 20 17 2 17 2 0 20 5 2 24 0 11 0 5 2 14 25 13 20 0 17 25 2 11 0 11 14 8 14 14 2 11 11 11 24 24 24 24 5 8 11 13 20 0 5 5 0 11 0 0 24 24 20 14 0 5 20 17 0 8 20 14 8 25 25 5 2 5 14091.0 1 0 1 0 0 1 0 0 1 0 0 1 0 1 1 1 0 1 0 0 1 0 1 0 1 1 0 0 1 0 13 11 13 2 13 5 2 24 8 17 25 8 8 20 17 14 14 17 5 11 2 0 22 0 22 8 2 24 5 22 5 25 13 11 20 20 24 15 0 14 0 14 15 14 24 14 0 13 11 8 11 14 14 5 20 20 17 25 0 14 0 5 20 0 11 11 24 0 22 22 13 13 11 17 24 8 24 20 2 24 20 0 20 17 2 17 17 0 20 5 2 24 17 11 0 24 2 8 25 8 8 17 17 25 2 25 17 11 14 28 14 14 2 11 11 25 24 24 5 24 5 8 25 13 13 17 5 15 0 11 0 25 24 5 8 14 17 5 20 17 0 8 20 14 8 25 25 5 2 5 12068.0 1 0 1 0 0 1 0 0 1 1 0 1 0 1 1 1 0 1 0 0 1 0 0 0 1 1 0 0 0 0 20 11 20 2 8 5 2 24 14 17 5 20 8 20 17 14 14 17 25 11 2 17 5 0 9 8 17 24 24 9 5 11 13 25 20 20 24 24 0 14 0 14 15 8 24 14 0 20 11 8 25 2 14 5 13 20 17 11 11 14 0 5 20 11 11 11 9 0 9 24 13 13 11 17 24 8 24 20 2 9 20 17 8 17 2 17 17 0 20 5 2 24 17 0 2 24 2 14 25 8 14 0 17 25 2 25 0 11 14 8 14 14 2 11 11 5 24 24 5 24 5 8 25 20 8 0 5 9 0 25 0 0 24 11 8 14 17 5 20 17 0 8 20 14 8 25 25 5 2 5 14841.0 1 0 1 0 0 1 0 0 1 1 0 1 0 1 1 1 0 1 0 0 1 0 0 0 1 1 0 0 0 0 13 25 20 2 8 5 2 24 14 17 24 20 8 20 17 14 17 17 25 11 14 0 0 0 25 8 2 24 24 9 25 25 8 25 20 20 24 15 0 14 0 14 15 8 24 14 25 13 25 8 0 8 14 5 13 20 17 11 11 14 0 5 20 11 11 11 5 0 25 9 13 13 11 17 24 8 9 20 14 5 13 0 13 17 0 17 17 11 20 5 2 24 17 11 17 5 2 11 5 13 8 17 17 11 2 25 0 0 14 8 14 14 2 11 25 11 24 24 24 24 5 13 11 20 13 0 5 15 17 5 0 11 24 24 8 14 0 11 20 17 0 8 20 14 8 25 11 5 2 5 16580.0 1 0 1 0 0 1 0 0 1 0 0 1 0 1 1 1 0 1 0 0 1 0 0 0 1 1 0 0 0 0 20 11 13 2 20 5 2 24 2 17 5 20 8 20 0 14 14 17 25 11 2 0 15 2 5 8 2 24 5 24 5 25 13 25 20 20 24 15 0 14 0 14 15 8 24 14 17 13 11 8 5 14 14 5 20 20 17 11 11 14 0 5 20 0 0 11 24 0 5 5 8 20 11 0 24 8 15 20 14 24 13 0 13 17 2 17 17 17 20 11 2 24 17 25 17 5 2 11 25 8 8 17 17 25 2 25 17 0 14 8 14 2 2 11 11 25 24 24 24 24 5 8 25 20 8 17 5 15 11 11 0 0 24 25 8 14 11 5 20 17 0 8 20 14 8 25 25 5 2 5 13090.0 1 0 1 0 0 1 1 0 1 0 0 1 0 0 1 1 0 1 0 0 1 0 0 0 1 1 0 0 1 0 20 11 20 2 20 5 2 24 14 17 24 8 8 20 17 14 14 17 5 11 2 0 5 17 5 8 2 24 5 24 5 25 6 25 20 20 24 15 0 14 0 14 15 8 24 14 0 20 11 8 5 14 14 5 20 20 17 11 11 14 0 5 20 11 11 11 24 0 5 5 20 6 11 17 24 8 24 20 14 24 20 0 20 17 17 17 17 17 20 5 2 24 17 11 17 5 2 14 25 8 8 17 17 25 2 25 17 11 14 28 14 14 2 11 11 25 24 15 24 24 5 8 25 20 8 0 5 24 0 25 0 11 24 5 8 14 17 5 20 17 0 8 20 14 8 11 25 5 2 5 11648.0 1 0 1 0 0 1 0 0 1 0 0 1 0 1 1 0 1 1 0 0 1 0 0 0 1 0 0 0 0 0 20 11 8 2 8 5 14 24 14 17 5 16 8 20 17 14 14 17 5 11 2 0 5 0 5 8 2 24 5 24 11 11 13 11 20 20 24 24 17 14 0 14 24 8 24 14 11 20 11 8 11 14 14 5 20 20 17 11 0 14 0 5 20 11 11 11 5 0 11 5 8 20 11 17 24 8 24 20 14 24 20 0 20 17 17 17 17 17 20 5 2 24 17 11 17 24 2 17 11 20 8 0 14 11 17 11 17 0 14 8 14 2 2 11 11 5 24 24 24 24 5 20 0 20 8 0 5 24 0 24 0 5 24 0 8 14 11 5 20 17 0 8 20 14 8 11 11 5 2 5 13718.0 1 0 1 0 0 1 0 0 1 0 0 1 0 0 1 1 0 1 0 0 1 0 0 0 1 1 0 0 0 0 20 11 20 2 8 5 2 24 14 17 24 20 8 20 2 14 14 17 5 11 2 0 5 17 5 8 2 24 5 24 5 25 8 25 20 20 24 24 11 14 17 14 15 14 24 14 0 20 25 8 0 14 14 5 20 20 17 17 25 14 0 5 20 11 0 11 24 0 5 5 8 20 11 17 24 8 24 20 14 24 20 0 20 17 2 17 17 17 20 25 2 24 2 11 17 5 14 14 11 14 8 17 17 25 2 25 17 0 14 8 14 2 2 11 11 17 24 24 24 24 5 8 25 20 8 0 5 24 11 11 0 11 24 5 8 14 11 5 20 17 0 8 20 14 8 5 25 5 2 5 16114.0 1 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 1 0 0 1 0 0 0 0 0 20 0 20 14 8 5 17 24 14 0 21 20 8 20 17 14 14 17 11 0 14 11 0 17 5 8 0 24 5 24 11 11 8 0 20 8 24 24 17 14 17 14 24 20 24 14 5 20 11 14 0 2 14 5 20 20 17 11 11 14 0 5 20 11 11 11 5 0 0 5 20 8 5 0 24 8 24 20 8 24 20 0 8 17 2 17 17 11 20 5 0 24 2 0 17 11 17 14 17 20 14 17 17 11 2 11 11 11 8 8 14 14 2 11 0 17 24 24 5 24 5 20 11 20 14 0 5 24 0 2 0 11 24 24 8 14 17 11 20 17 0 8 20 14 8 0 11 5 2 5 12566.0 1 0 1 0 0 1 0 0 1 0 0 1 0 0 1 1 0 1 0 0 1 0 0 0 1 0 0 0 0 1 20 11 20 14 20 5 2 24 14 17 24 20 20 20 17 14 14 17 5 11 14 0 24 17 24 8 17 24 24 24 5 11 20 11 20 20 24 24 0 14 0 14 15 8 24 14 17 20 11 8 11 14 14 5 20 20 17 17 11 14 0 5 20 11 11 11 24 0 24 24 20 20 11 17 24 8 24 20 14 24 20 17 20 17 17 17 17 17 20 5 17 24 17 11 17 24 2 5 11 20 8 17 17 11 2 29 17 11 14 8 14 14 2 11 11 29 24 24 24 24 5 20 11 20 20 17 5 24 17 11 0 0 24 5 8 14 0 5 20 17 0 8 20 14 8 11 11 5 2 5 11488.0 1 0 1 0 0 1 0 0 1 0 0 1 0 0 1 1 0 1 0 0 1 0 0 0 1 1 0 0 0 0 20 0 20 14 20 5 17 24 14 17 24 8 8 20 17 14 14 17 5 11 2 17 24 17 24 8 2 24 24 24 24 25 20 11 20 20 24 24 0 14 0 14 15 2 24 14 0 20 25 8 5 14 14 5 20 20 17 11 11 14 0 5 20 11 11 11 24 0 5 5 20 20 11 17 24 8 24 20 14 24 20 17 20 17 17 17 17 17 20 5 2 24 17 0 17 24 14 17 11 20 20 17 17 11 2 25 17 11 14 14 14 14 2 11 11 17 24 24 24 24 5 8 0 20 20 0 5 24 0 11 0 11 24 5 14 14 17 5 20 17 0 20 20 14 8 11 25 5 2 5 13643.0 1 0 1 0 0 1 0 0 1 0 0 1 0 0 1 1 0 1 0 0 1 0 0 0 1 1 0 0 0 0 20 11 20 14 20 5 2 24 14 17 5 20 14 20 17 14 14 17 5 11 14 17 5 17 5 20 2 24 5 24 5 11 20 5 20 20 24 24 25 14 0 14 15 8 24 14 17 20 11 8 5 14 14 5 20 20 17 11 17 14 0 5 20 11 11 11 24 0 5 5 20 20 11 17 24 8 24 20 14 24 20 17 20 17 17 17 17 0 20 5 2 24 17 11 17 5 2 2 11 8 8 17 17 11 2 25 17 11 14 8 14 2 2 11 11 11 24 24 24 24 5 8 25 20 20 0 5 24 11 17 0 0 24 24 8 14 0 5 20 17 0 8 20 14 8 25 25 5 17 5 14608.0 1 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 0 1 0 0 0 0 0 20 11 20 2 20 5 2 24 14 17 24 8 20 20 17 14 14 17 5 11 14 0 5 17 24 8 2 24 24 24 5 5 8 11 20 20 24 24 17 14 0 14 24 8 24 14 11 20 11 8 5 14 14 5 20 20 17 11 11 14 0 5 20 11 11 11 24 0 24 24 8 20 11 17 24 8 5 20 14 5 20 0 20 17 17 17 17 0 20 5 2 24 2 11 17 24 14 17 17 8 20 17 17 11 17 5 17 11 14 8 14 14 2 11 11 5 24 24 24 24 5 20 11 20 8 11 5 24 17 11 0 17 24 24 8 14 11 5 20 17 0 8 20 14 8 11 11 5 2 5 7117.0 1 1 1 1 1 1 1 1 1 1 5 6 4 1 7 9 3 7 8 8 4 9 2 6 2 7 7 3 0 6 6 1 4 1 6 5 5 4 8 5 8 0 0 0 3 9 2 6 6 9 2 7 3 3 3 5 0 5 6 4 1 1 6 6 2 7 2 8 8 8 0 0 1 5 0 6 6 6 4 4 0 4 4 0 7 7 2 2 6 5 7 2 2 9 9 7 9 9 9 2 8215.0 0 1 0 0 0 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 12 10 11 9 19 18 16 13 17 1 11 19 19 10 19 19 12 13 14 14 14 9 9 11 12 8 11 12 17 13 1 19 6 19 14 19 19 15 1 1 1 11 11 16 9 14 18 19 15 19 1 17 1 13 13 9 17 14 14 14 11 11 11 13 11 13 15 15 19 19 19 18 19 19 19 18 19 19 19 19 9143.0 1 0 1 0 0 1 0 0 0 0 0 1 1 1 0 1 0 1 1 0 0 1 1 0 1 1 1 1 1 1 27 22 12 24 25 22 26 21 27 5 24 24 13 17 18 18 21 15 12 0 25 26 22 22 17 13 29 18 27 5 29 24 2 26 0 22 25 27 15 11 18 18 27 24 29 25 26 17 17 11 21 0 0 18 18 18 21 22 2 27 5 27 24 29 27 28 27 27 24 27 7142.0 1 1 1 1 1 1 1 1 1 1 4 4 4 4 4 4 4 4 6 0 0 0 0 0 0 0 1 1 1 7 1 1 6 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 4 0 1 1 2 8 7 5 9 7 7 6 5 5 8 8 7 7 9 5 5 5 9 3 0 6 6 8 1 5 4 7 9 7 7 8 6 6 5 8 7 9 9 9 9 9 6 5 7 5 8385.0 1 0 0 1 1 1 1 0 0 0 1 0 1 0 0 1 1 1 1 1 0 0 0 0 0 0 0 6 18 6 6 6 18 6 6 3 3 3 3 3 10 4 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 0 0 3 3 4 3 18 16 18 3 18 19 3 18 17 17 18 5 5 15 3 10 3 3 15 17 18 3 17 12 3 3 18 4 19 17 18 17 17 16 10 3 9484.0 1 0 1 1 0 1 1 1 1 1 1 1 0 0 1 0 0 0 1 1 0 0 0 0 1 0 1 1 1 0 10 10 0 10 8 0 0 11 11 11 11 11 11 11 11 2 3 2 2 24 2 6 6 6 6 5 5 7 9 8 8 8 9 7 0 2 3 5 14 11 28 18 19 0 24 3 26 27 27 28 0 27 27 11 3 26 26 18 14 18 19 19 28 26 27 27 14 27 19 18 6429.0 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 2 0 1 2 1 1 2 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 5 5 5 5 5 5 5 6 6 6 6 6 6 6 6 7 7 6 7 6 0 7 7 7 7 7 7 8 8 8 6 6 8 6 8 9 9 9 8 0 9 9 9 7605.0 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 0 0 1 0 1 0 1 0 0 0 1 1 1 1 2 3 2 2 3 2 4 5 4 5 4 5 5 5 5 6 6 6 6 6 6 6 9 9 8 8 9 9 9 9 4 9 9 10 10 11 10 11 10 13 13 12 12 13 12 12 14 14 12 14 12 0 14 14 14 14 17 17 17 17 17 17 17 17 19 19 19 19 19 19 19 8550.0 0 0 1 1 0 1 1 1 0 1 1 0 1 0 0 1 0 1 0 1 1 0 0 1 0 1 1 0 1 1 2 2 2 2 2 2 2 2 3 5 3 3 5 6 15 6 7 7 7 6 7 7 9 9 9 9 10 12 12 20 12 12 15 15 17 12 17 17 17 17 12 17 20 20 20 19 20 20 23 17 23 19 2 23 23 23 23 26 26 26 25 26 26 25 28 29 28 28 28 29 9197.0 1 1 1 1 0 1 1 1 0 1 7 6 3 0 2 1 5 5 3 7 7 0 6 9 0 9 9 5 5 3 6 6 0 3 0 6 7 3 3 7 7 7 3 3 3 5 1 9 0 6 6 9 9 9 5 5 5 5 5 3 3 5 6 0 0 0 6 1 5 2 2 7 7 7 3 7 0 3 3 7 3 6 6 0 6 3 3 3 3 0 3 5 2 2 2 2 1 7 5 7 9 1 1 2 6 1 9 1 1 9 8386.0 0 1 0 0 0 1 1 0 0 0 1 0 1 1 1 1 0 0 1 1 15 6 19 10 12 13 5 5 15 1 5 10 6 13 6 13 12 5 5 15 14 10 10 14 14 14 15 19 19 15 1 1 19 19 15 5 13 13 10 6 10 13 13 13 5 5 5 5 5 19 15 5 10 14 14 10 6 14 13 13 13 5 1 5 15 15 10 14 1 15 15 10 10 10 10 19 19 19 19 19 15 5 12 12 18 13 18 5 5 1 13 18 13 12 18 13 13 18 13 13 9272.0 0 0 0 1 1 1 1 1 1 1 7 7 7 7 7 7 7 7 4 4 4 4 3 4 4 4 4 8 3 8 8 8 3 8 3 9 9 9 9 9 9 9 9 5 6 6 6 6 6 6 6 6 6 6 7 7 4 8 8 5 5 3 4 6 4 5 3 3 3 4 6 5 5 3 3 3 5 7 4 7 6 6 4 3 3 5 3 4 7 6 6 3 3 5 5 3 3 7 5 7 5 5 6 5 5 6 3 4 3 7 9427.0 0 1 0 1 0 1 0 1 0 1 0 0 1 1 0 1 0 1 1 0 5 5 5 5 5 5 5 5 1 7 7 7 7 7 7 7 7 12 13 13 12 13 13 12 12 9 9 9 9 9 9 9 9 15 18 18 18 18 18 18 18 18 18 18 5 5 7 12 12 15 15 1 7 18 7 15 3 3 1 7 18 15 15 13 13 3 15 18 7 5 17 18 7 3 3 15 3 7 5 15 18 1 13 15 15 3 3 7 15 1 13 15 17 17 15 15 1 7 3 5 9320.0 1 0 1 1 1 0 1 1 1 1 7 2 7 7 7 2 7 2 7 2 2 2 2 2 2 2 2 2 2 3 3 3 2 3 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 8 6 8 6 8 6 8 3 8 3 3 3 3 6 6 6 6 6 6 6 6 6 0 4 6 6 6 0 6 6 0 0 6 0 4 7 0 0 0 0 0 0 4 4 4 4 4 4 4 4 4 4 9 4 9 9 9 9 9 2 7 8509.0 0 0 1 0 0 0 1 1 1 1 0 0 1 1 0 1 0 0 1 0 18 18 18 18 18 2 18 13 8 2 2 8 8 8 2 8 2 2 2 7 6 2 2 6 2 12 2 6 6 6 6 6 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 9 13 7 13 7 9 7 7 13 13 7 9 13 18 13 9 13 9 9 13 15 15 15 15 15 15 15 15 18 18 18 18 18 18 18 18 18 18 18 23882.0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 18 19 21 27 2 12 15 27 22 28 4 28 13 1 28 18 1 20 22 0 18 16 5 24 10 18 27 19 29 2 19 19 4 16 27 6 20 8 24 16 18 9 6 4 8 19 13 21 19 14 24 16 24 16 8 6 11 9 18 7 29 0 7 8 13 11 5 14 28 25 18 12 19 9 23 23 20 23 8 16 10 0 9 25 29 13 6 5 27 7 28 6 25 25 2 3 11 6 0 28 28 15 25 18 4 0 4 24 22 3 19 4 27 12 12 8 28 7 17 8 26 11 17 19 26 16 2 11 26 17 28 24 25 20 23 9 3 3 28 10 12 22 11 11 25 5 24 15 27 25 24 17 23 0 8 11 2 15 3 13 13 27 10 24 5 27 5 4 3 21 1 20 19 14 26 2 0 10 29 10 5 21 2 19 12 8 28 23 0 21 2 21 6 4 7 12 7 15 10 19 32882.0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 18 19 21 27 2 12 15 27 22 28 4 28 13 1 28 18 1 20 22 0 18 16 5 24 10 18 27 19 29 2 19 19 4 16 27 6 20 8 24 16 18 9 6 4 8 19 13 21 19 14 24 16 24 16 8 6 11 9 18 7 29 0 7 8 13 11 5 14 28 25 18 12 19 9 23 23 20 23 8 16 10 0 9 25 29 13 6 5 27 7 28 6 25 25 2 3 11 6 0 28 28 15 25 18 4 0 4 24 22 3 19 4 27 12 12 8 28 7 17 8 26 11 17 19 26 16 2 11 26 17 28 24 25 20 23 9 3 3 28 10 12 22 11 11 25 5 24 15 27 25 24 17 23 0 8 11 2 15 3 13 13 27 10 24 5 27 5 4 3 21 1 20 19 14 26 2 0 10 29 10 5 21 2 19 12 8 28 23 0 21 2 21 6 4 7 12 7 15 10 19 53219.0 1 0 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 18 6 21 27 2 12 15 27 22 28 4 28 8 5 28 18 3 8 22 0 18 16 5 24 10 18 27 28 29 21 19 19 4 15 27 6 9 8 24 16 18 9 6 26 8 19 3 21 19 14 27 16 24 16 8 6 18 9 18 11 4 0 7 8 10 11 5 14 28 25 7 12 6 9 23 10 10 5 9 16 23 17 9 7 29 3 6 5 27 7 28 6 18 25 5 3 28 25 0 28 28 15 12 18 4 24 4 24 22 3 19 4 27 12 12 8 28 7 17 8 9 11 28 19 26 16 16 11 26 17 28 24 11 10 23 9 3 3 28 10 12 27 6 25 12 5 24 15 18 25 24 17 5 0 8 27 5 15 3 3 23 27 10 2 23 27 2 29 3 21 23 8 28 10 26 2 27 8 29 10 2 24 24 19 12 8 11 23 17 2 21 21 19 29 7 6 7 21 23 19 39121.0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 18 19 21 27 2 12 15 27 22 28 4 28 13 1 28 18 1 20 22 0 18 16 5 24 10 18 27 19 29 2 19 19 4 16 27 6 20 8 24 16 18 9 6 4 8 19 13 21 19 14 24 16 24 16 8 6 11 9 18 7 29 0 7 8 13 11 5 14 28 25 18 12 19 9 23 23 20 23 8 16 10 0 9 25 29 13 6 5 27 7 28 6 25 25 2 3 11 6 0 28 28 15 25 18 4 0 4 24 22 3 19 4 27 12 12 8 28 7 17 8 26 11 17 19 26 16 2 11 26 17 28 24 25 20 23 9 3 3 28 10 12 22 11 11 25 5 24 15 27 25 24 17 23 0 8 11 2 15 3 13 13 27 10 24 5 27 5 4 3 21 1 20 19 14 26 2 0 10 29 10 5 21 2 19 12 8 28 23 0 21 2 21 6 4 7 12 7 15 10 19 23882.0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 18 19 21 27 2 12 15 27 22 28 4 28 13 1 28 18 1 20 22 0 18 16 5 24 10 18 27 19 29 2 19 19 4 16 27 6 20 8 24 16 18 9 6 4 8 19 13 21 19 14 24 16 24 16 8 6 11 9 18 7 29 0 7 8 13 11 5 14 28 25 18 12 19 9 23 23 20 23 8 16 10 0 9 25 29 13 6 5 27 7 28 6 25 25 2 3 11 6 0 28 28 15 25 18 4 0 4 24 22 3 19 4 27 12 12 8 28 7 17 8 26 11 17 19 26 16 2 11 26 17 28 24 25 20 23 9 3 3 28 10 12 22 11 11 25 5 24 15 27 25 24 17 23 0 8 11 2 15 3 13 13 27 10 24 5 27 5 4 3 21 1 20 19 14 26 2 0 10 29 10 5 21 2 19 12 8 28 23 0 21 2 21 6 4 7 12 7 15 10 19 32882.0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 18 19 21 27 2 12 15 27 22 28 4 28 13 1 28 18 1 20 22 0 18 16 5 24 10 18 27 19 29 2 19 19 4 16 27 6 20 8 24 16 18 9 6 4 8 19 13 21 19 14 24 16 24 16 8 6 11 9 18 7 29 0 7 8 13 11 5 14 28 25 18 12 19 9 23 23 20 23 8 16 10 0 9 25 29 13 6 5 27 7 28 6 25 25 2 3 11 6 0 28 28 15 25 18 4 0 4 24 22 3 19 4 27 12 12 8 28 7 17 8 26 11 17 19 26 16 2 11 26 17 28 24 25 20 23 9 3 3 28 10 12 22 11 11 25 5 24 15 27 25 24 17 23 0 8 11 2 15 3 13 13 27 10 24 5 27 5 4 3 21 1 20 19 14 26 2 0 10 29 10 5 21 2 19 12 8 28 23 0 21 2 21 6 4 7 12 7 15 10 19 53882.0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 18 19 21 27 2 12 15 27 22 28 4 28 13 1 28 18 1 20 22 0 18 16 5 24 10 18 27 19 29 2 19 19 4 16 27 6 20 8 24 16 18 9 6 4 8 19 13 21 19 14 24 16 24 16 8 6 11 9 18 7 29 0 7 8 13 11 5 14 28 25 18 12 19 9 23 23 20 23 8 16 10 0 9 25 29 13 6 5 27 7 28 6 25 25 2 3 11 6 0 28 28 15 25 18 4 0 4 24 22 3 19 4 27 12 12 8 28 7 17 8 26 11 17 19 26 16 2 11 26 17 28 24 25 20 23 9 3 3 28 10 12 22 11 11 25 5 24 15 27 25 24 17 23 0 8 11 2 15 3 13 13 27 10 24 5 27 5 4 3 21 1 20 19 14 26 2 0 10 29 10 5 21 2 19 12 8 28 23 0 21 2 21 6 4 7 12 7 15 10 19 39121.0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 18 19 21 27 2 12 15 27 22 28 4 28 13 1 28 18 1 20 22 0 18 16 5 24 10 18 27 19 29 2 19 19 4 16 27 6 20 8 24 16 18 9 6 4 8 19 13 21 19 14 24 16 24 16 8 6 11 9 18 7 29 0 7 8 13 11 5 14 28 25 18 12 19 9 23 23 20 23 8 16 10 0 9 25 29 13 6 5 27 7 28 6 25 25 2 3 11 6 0 28 28 15 25 18 4 0 4 24 22 3 19 4 27 12 12 8 28 7 17 8 26 11 17 19 26 16 2 11 26 17 28 24 25 20 23 9 3 3 28 10 12 22 11 11 25 5 24 15 27 25 24 17 23 0 8 11 2 15 3 13 13 27 10 24 5 27 5 4 3 21 1 20 19 14 26 2 0 10 29 10 5 21 2 19 12 8 28 23 0 21 2 21 6 4 7 12 7 15 10 19 23882.0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 18 19 21 27 2 12 15 27 22 28 4 28 13 1 28 18 1 20 22 0 18 16 5 24 10 18 27 19 29 2 19 19 4 16 27 6 20 8 24 16 18 9 6 4 8 19 13 21 19 14 24 16 24 16 8 6 11 9 18 7 29 0 7 8 13 11 5 14 28 25 18 12 19 9 23 23 20 23 8 16 10 0 9 25 29 13 6 5 27 7 28 6 25 25 2 3 11 6 0 28 28 15 25 18 4 0 4 24 22 3 19 4 27 12 12 8 28 7 17 8 26 11 17 19 26 16 2 11 26 17 28 24 25 20 23 9 3 3 28 10 12 22 11 11 25 5 24 15 27 25 24 17 23 0 8 11 2 15 3 13 13 27 10 24 5 27 5 4 3 21 1 20 19 14 26 2 0 10 29 10 5 21 2 19 12 8 28 23 0 21 2 21 6 4 7 12 7 15 10 19 32882.0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 18 19 21 27 2 12 15 27 22 28 4 28 13 1 28 18 1 20 22 0 18 16 5 24 10 18 27 19 29 2 19 19 4 16 27 6 20 8 24 16 18 9 6 4 8 19 13 21 19 14 24 16 24 16 8 6 11 9 18 7 29 0 7 8 13 11 5 14 28 25 18 12 19 9 23 23 20 23 8 16 10 0 9 25 29 13 6 5 27 7 28 6 25 25 2 3 11 6 0 28 28 15 25 18 4 0 4 24 22 3 19 4 27 12 12 8 28 7 17 8 26 11 17 19 26 16 2 11 26 17 28 24 25 20 23 9 3 3 28 10 12 22 11 11 25 5 24 15 27 25 24 17 23 0 8 11 2 15 3 13 13 27 10 24 5 27 5 4 3 21 1 20 19 14 26 2 0 10 29 10 5 21 2 19 12 8 28 23 0 21 2 21 6 4 7 12 7 15 10 19 52779.0 1 1 1 1 1 1 1 1 1 0 1 0 1 0 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 18 6 16 27 21 12 15 27 22 28 4 28 8 1 19 18 1 14 22 27 18 16 5 24 23 7 27 19 29 5 19 19 4 16 27 6 8 8 24 16 18 8 12 4 8 4 8 2 19 20 0 16 2 21 8 12 7 26 18 7 29 0 7 8 3 12 0 14 28 25 25 12 19 8 23 23 10 10 8 22 23 0 26 25 29 10 6 5 27 12 27 6 12 25 0 3 27 28 0 28 28 21 12 18 4 0 4 24 22 3 19 4 18 12 12 8 28 7 14 8 20 25 27 19 26 16 21 12 19 0 12 24 25 3 1 26 3 3 28 20 12 27 12 25 25 5 24 21 27 25 0 0 23 0 8 28 2 21 3 3 23 27 10 24 10 27 5 4 3 21 5 8 28 10 26 21 0 20 29 10 5 21 24 19 25 8 28 23 0 21 2 21 19 6 25 12 7 21 10 6 23882.0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 18 19 21 27 2 12 15 27 22 28 4 28 13 1 28 18 1 20 22 0 18 16 5 24 10 18 27 19 29 2 19 19 4 16 27 6 20 8 24 16 18 9 6 4 8 19 13 21 19 14 24 16 24 16 8 6 11 9 18 7 29 0 7 8 13 11 5 14 28 25 18 12 19 9 23 23 20 23 8 16 10 0 9 25 29 13 6 5 27 7 28 6 25 25 2 3 11 6 0 28 28 15 25 18 4 0 4 24 22 3 19 4 27 12 12 8 28 7 17 8 26 11 17 19 26 16 2 11 26 17 28 24 25 20 23 9 3 3 28 10 12 22 11 11 25 5 24 15 27 25 24 17 23 0 8 11 2 15 3 13 13 27 10 24 5 27 5 4 3 21 1 20 19 14 26 2 0 10 29 10 5 21 2 19 12 8 28 23 0 21 2 21 6 4 7 12 7 15 10 19 32882.0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 18 19 21 27 2 12 15 27 22 28 4 28 13 1 28 18 1 20 22 0 18 16 5 24 10 18 27 19 29 2 19 19 4 16 27 6 20 8 24 16 18 9 6 4 8 19 13 21 19 14 24 16 24 16 8 6 11 9 18 7 29 0 7 8 13 11 5 14 28 25 18 12 19 9 23 23 20 23 8 16 10 0 9 25 29 13 6 5 27 7 28 6 25 25 2 3 11 6 0 28 28 15 25 18 4 0 4 24 22 3 19 4 27 12 12 8 28 7 17 8 26 11 17 19 26 16 2 11 26 17 28 24 25 20 23 9 3 3 28 10 12 22 11 11 25 5 24 15 27 25 24 17 23 0 8 11 2 15 3 13 13 27 10 24 5 27 5 4 3 21 1 20 19 14 26 2 0 10 29 10 5 21 2 19 12 8 28 23 0 21 2 21 6 4 7 12 7 15 10 19 53168.0 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 18 19 21 27 2 12 15 28 22 11 4 28 20 1 28 7 3 20 22 27 27 16 5 24 23 7 27 19 29 2 19 19 4 16 27 6 8 8 24 15 18 9 6 29 8 19 3 21 19 14 0 16 2 16 8 12 11 9 7 7 29 0 7 20 13 11 0 14 6 25 18 12 19 9 23 23 20 23 8 24 23 0 9 7 29 13 19 2 27 7 28 6 7 25 2 3 11 6 0 28 28 15 25 7 4 0 4 24 22 3 19 4 27 12 12 8 28 25 28 8 20 11 28 19 26 16 2 11 19 0 28 2 12 20 23 9 3 3 28 23 12 24 6 11 25 5 24 21 27 25 24 0 23 0 8 11 2 21 3 3 13 27 23 24 5 27 5 4 3 21 5 20 28 14 4 21 0 20 29 20 5 15 24 28 12 8 28 23 0 21 2 21 6 4 7 12 7 21 23 19 39121.0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 18 19 21 27 2 12 15 27 22 28 4 28 13 1 28 18 1 20 22 0 18 16 5 24 10 18 27 19 29 2 19 19 4 16 27 6 20 8 24 16 18 9 6 4 8 19 13 21 19 14 24 16 24 16 8 6 11 9 18 7 29 0 7 8 13 11 5 14 28 25 18 12 19 9 23 23 20 23 8 16 10 0 9 25 29 13 6 5 27 7 28 6 25 25 2 3 11 6 0 28 28 15 25 18 4 0 4 24 22 3 19 4 27 12 12 8 28 7 17 8 26 11 17 19 26 16 2 11 26 17 28 24 25 20 23 9 3 3 28 10 12 22 11 11 25 5 24 15 27 25 24 17 23 0 8 11 2 15 3 13 13 27 10 24 5 27 5 4 3 21 1 20 19 14 26 2 0 10 29 10 5 21 2 19 12 8 28 23 0 21 2 21 6 4 7 12 7 15 10 19 总结 和TSP问题很相似,寻找最优的时候,初始化可以使用贪心策略来进行初始解的生成,不过在这里遇到了一个坑,那就是我错估了贪心的效果了,没想到使用了贪心策略之后,得到的解已经很接近最优了,因此在使用模拟退火和遗传算法的时候,反而得到了一个更加糟糕的结果,所以要选择一个比较低的温度,因为已经比较接近最优了,就不要接受太多的差解了,如果接受太多差解的话,有可能导致之后回不到刚开始更好的位置了。 在这次的实验中我也学到了交换方式是多么的重要,一开始因为做惯了TSP问题,想着是随机挑两个工厂交换一对消费者,结果一直得不到好的结果,因为我都用了贪心的算法,让他们选择了当前较好的位置,这样交换就一定会得到很坏的结果。后来我换了一种方式,只移动一个消费者,这样的变动就随机多了,而且通常影响不大,更有利于在目前已经很稳定的局面下寻找一个更好的解。","tags":[{"name":"Algorithms","slug":"Algorithms","permalink":"https://zhangziquan.github.io/tags/Algorithms/"},{"name":"TSP","slug":"TSP","permalink":"https://zhangziquan.github.io/tags/TSP/"}]},{"title":"[LeetCode] 732. My Calendar III","date":"2018-12-06T13:01:00.000Z","path":"2018/12/06/LeetCode-732-My-Calendar-III/","text":"732. My Calendar III week13 难度:Hard 题目链接 题目描述 Implement a MyCalendarThree class to store your events. A new event can always be added. Your class will have one method, book(int start, int end). Formally, this represents a booking on the half open interval [start, end), the range of real numbers x such that start <= x < end. A K-booking happens when K events have some non-empty intersection (ie., there is some time that is common to all K events.) For each call to the method MyCalendar.book, return an integer K representing the largest integer such that there exists a K-booking in the calendar. Your class will be called like this: MyCalendarThree cal = new MyCalendarThree(); MyCalendarThree.book(start, end) Example1: MyCalendarThree(); MyCalendarThree.book(10, 20); // returns 1 MyCalendarThree.book(50, 60); // returns 1 MyCalendarThree.book(10, 40); // returns 2 MyCalendarThree.book(5, 15); // returns 3 MyCalendarThree.book(5, 10); // returns 3 MyCalendarThree.book(25, 55); // returns 3 Explanation: The first two events can be booked and are disjoint, so the maximum K-booking is a 1-booking. The third event [10, 40) intersects the first event, and the maximum K-booking is a 2-booking. The remaining events cause the maximum K-booking to be only a 3-booking. Note that the last event locally causes a 2-booking, but the answer is still 3 because eg. [10, 20), [10, 40), and [5, 15) are still triple booked. Note: The number of calls to MyCalendarThree.book per test case will be at most 400. In calls to MyCalendarThree.book(start, end), start and end are integers in the range [0, 10^9]. 题目分析 题目简单来说,就是寻找最大的重合数量,可以看作为有许多条线段在一个区间上,每加入一条线段则要得出最大的重叠数量,重叠就是指线段有一段重合的区间则这两条线段重叠。 解题思路 这道题我想的是如果要求最大重叠数量,假如对每条线段都去遍历一遍得出重合的线段再统计的话就很麻烦,即使维护一个最大数量每次插入后比较,也并不会很好做。 对于这个题目可以想到这么一种做法,记录下每一条线段的起点与终点,然后在这一个区间上遍历,如果遇到起点,那么此时有一条线段加入,如果遇到终点,即意味着有一条线段结束了,通过这种做法可以得出在某个点上线段有多少条,求出最大的即可。 在数据结构上选择map可以不用考虑排序的问题,而且键值对便于修改。加入线段,线段的起点数值+1,终点-1,这样在遍历这整个区间时,直接加上该点的数值即可方便统计线段数量。 代码 123456789101112131415161718192021222324252627282930class MyCalendarThree{ public: map<int, int> m; int maximum; MyCalendarThree() { maximum = 0; } int book(int start, int end) { int num = 0; m[start]++; m[end]--; for (auto i = m.begin(); i != m.end(); ++i) { num += i->second; if (maximum < num) maximum = num; } return maximum; }};/** * Your MyCalendarThree object will be instantiated and called as such: * MyCalendarThree obj = new MyCalendarThree(); * int param_1 = obj.book(start,end); */ 结果反思  这个算法虽然运行速度不快,但是胜于非常简单。。很容易理解。代码也非常简洁,就那么几行搞定,主要是要选择适合的数据结构存储。 总结 这个方法其实是有向图的一个变形,用到了出度和入度的方法,就和我说的初始和闭合一样,到初始点数量++,到了终点数量–。所以最大数量总是在某个线段开始的地方的,因此就转变为了寻找最大的出度。","tags":[{"name":"Algorithms","slug":"Algorithms","permalink":"https://zhangziquan.github.io/tags/Algorithms/"},{"name":"LeetCode","slug":"LeetCode","permalink":"https://zhangziquan.github.io/tags/LeetCode/"},{"name":"Graph","slug":"Graph","permalink":"https://zhangziquan.github.io/tags/Graph/"}]},{"title":"[LeetCode] 312. Burst Balloons","date":"2018-11-27T15:20:00.000Z","path":"2018/11/27/LeetCode-312-Burst-Balloons/","text":"312. Burst Balloons week12 难度:Hard 题目链接 题目描述 Given n balloons, indexed from 0 to n-1. Each balloon is painted with a number on it represented by array nums. You are asked to burst all the balloons. If the you burst balloon i you will get nums[left] * nums[i] * nums[right] coins. Here left and right are adjacent indices of i. After the burst, the left and right then becomes adjacent. Find the maximum coins you can collect by bursting the balloons wisely. Note: You may imagine nums[-1] = nums[n] = 1. They are not real therefore you can not burst them. 0 ≤ n ≤ 500, 0 ≤ nums[i] ≤ 100 Example: Input: [3,1,5,8] Output: 167 Explanation: nums = [3,1,5,8] --> [3,5,8] --> [3,8] --> [8] --> [] coins = 3*1*5 + 3*5*8 + 1*3*8 + 1*8*1 = 167 题目分析 题意就是给予一连串的气球,每个气球上都有分数,当点击气球后,气球消失,获得的分数即是被点击的气球*两边的气球。找出可获得的最大分数。 解题思路 这道题明示使用动态规划,考虑将问题分为多个子问题解决,假设按点击的顺序计算,每次点击都会使被点击的气球消失,再根据剩下的元素来构造新的数组,那么这个问题又变成了新的环境,难以简单地复用解决。假设按倒推的方式思考,假设最后一个气球被戳破,那么它左边或者右边的气球不会受到对方的影响,因为隔着一个气球始终在它们的中间,所以左边和右边的气球戳破顺序就可以分开计算,化简成多个小问题。 假设第i个气球为最后戳破的气球,max[a][b]为第a到第b个气球获得的最大分数,那么问题的解就变为:$$maxCoins[0][n - 1] = maxCoins[0][i - 1] + maxCoins[i + 1][n - 1] + nums[left] * nums[i] * nums[right]$$ 代码 123456789101112131415161718192021class Solution{public: int maxCoins(vector<int> &nums) { int n = nums.size(); vector<vector<int>> dp(n, vector<int>(n, 0)); for (int i = 2; i < n; i++) { for (int l = 0; l < n - i; l++) { int r = l + i; for (int j = l + 1; j < r; j++) { dp[l][r] = max(dp[l][r], dp[l][j] + dp[j][r] + nums[l] * nums[r] * nums[j]); } } } return dp[0][n - 1]; }}; 结果反思 测试 这个算法还是比较简洁的,没有什么多余的东西,就是纯dp。 代码分析 因为要考虑到戳破气球时,有边界的问题,所以在两边加上两个无法被戳破的分数为1的气球,构成n+2的新数组,最后的状态转移为:$$Coins[left][right] = max(Coins[left][right], Coins[left][i] + Coins[i][right] + nums[left] * nums[right] * nums[i]);$$ 总结 这个题目说明了动态规划有时候按正向推导分解问题可能会分解不了多个较小的子问题,在这种时候要尝试逆向思维,反过来思考到达最后的状态时,要经过哪些状态。这道题有点和上两周那道Freedom Trail即辐射4旋转字符串一样,都是从最后的状态开始,从而分解成多个子问题。","tags":[{"name":"Algorithms","slug":"Algorithms","permalink":"https://zhangziquan.github.io/tags/Algorithms/"},{"name":"LeetCode","slug":"LeetCode","permalink":"https://zhangziquan.github.io/tags/LeetCode/"},{"name":"Dynamic Programming","slug":"Dynamic-Programming","permalink":"https://zhangziquan.github.io/tags/Dynamic-Programming/"}]},{"title":"[LeetCode] 354. Russian Doll Envelopes","date":"2018-11-17T08:45:00.000Z","path":"2018/11/17/LeetCode-354-Russian-Doll-Envelopes/","text":"354. Russian Doll Envelopes week11 难度:Hard 题目链接 题目描述 You have a number of envelopes with widths and heights given as a pair of integers (w, h). One envelope can fit into another if and only if both the width and height of one envelope is greater than the width and height of the other envelope. What is the maximum number of envelopes can you Russian doll? (put one inside other) Note: Rotation is not allowed. Example: Input: [[5,4],[6,4],[6,7],[2,3]] Output: 3 Explanation: The maximum number of envelopes you can Russian doll is 3 ([2,3] => [5,4] => [6,7]). 题目分析 再来一道动态规划的题目,本题是一道变形的最长子串题目,讲的是俄罗斯套娃那种排序,N个信封给出高和宽,若一个信封的高和宽都大于另一个信封,则可以装进去,目标求最长的序列。 解题思路 这是一道比较常见的动态规划问题,记录每一个信封的最大装载数量,若有信封能装进该信封,那么比较装载数量,然后逐步找到最大装载数量的信封即可。 将信封从小到大排序,宽度从小到大,宽度相同则高度从小到大,对后面的信封都要遍历前面的信封,看是否能装进去,然后更新dp,每次都比较下信封的最大装载量。 代码 123456789101112131415161718192021222324class Solution{ public: int maxEnvelopes(vector<pair<int, int>> &envelopes) { vector<int> dp(envelopes.size(), 1); pair<int, int> temp; sort(envelopes.begin(), envelopes.end()); int maxnum = 0; for (int i = 0; i < envelopes.size(); i++) { for (int j = 0; j < i; j++) { if (envelopes[i].first > envelopes[j].first && envelopes[i].second > envelopes[j].second) { dp[i] = max(dp[i], dp[j] + 1); } } maxnum = max(maxnum, dp[i]); } return maxnum; }}; 结果反思 测试 从结果看来这种做法算是中规中矩,就是不断地去遍历,对信封的装载数量进行不断更新,最后达到收敛,就像Bellman-Ford算法一样不断更新,而且这个好像更加慢。。 目前最佳解法 1234567891011121314151617181920212223242526bool cmp(const pair<int, int> &p1, const pair<int, int> &p2) { if (p1.first < p2.first) { return true; } else if (p1.first == p2.first) { return p1.second > p2.second; } return false;}bool cmp2(const pair<int, int> &p1, const pair<int, int> &p2) { return p1.second < p2.second;}class Solution {public: int maxEnvelopes(vector<pair<int, int>>& envelopes) { vector<pair<int, int>> dp; sort(envelopes.begin(), envelopes.end(), cmp); for (const pair<int, int> &p : envelopes) { auto iter = lower_bound(dp.begin(), dp.end(), p, cmp2); if (iter == dp.end()) { dp.push_back(p); } else { dp[iter - dp.begin()] = p; } } return dp.size(); }}; 代码分析 第二种解法,这个似乎是现在最多且很快的解法,和求最长递增子串的长度相类似,运用了其中的思想,也就是贪心和二分的想法,先对宽度从小到大进行排序,这样能保证后面的信封能装进前面的信封,然后对高度进行最长递增子串寻找最长,这样就能得到最长,也许会出现宽度相同的情况,但是这也说明了最长递增子串这个问题的想法,得到的解并不会是正确的解,但是长度是相同的,对较大的进行置换成小的使得它能够得到更大的潜力去递增,这就是贪心的策略,在贪心的同时并没有改变其中的长度,很精妙。。。 总结 这个题目说明了动态规划有时候并不会是最简单的,但也是比较快的了,动态规划能够避免许多小问题的重复求解,在求得对应的序列比递归有很大的优势,在一些问题归纳若子问题的规模仍很大,那么动态规划比递归好很多。第二种的贪心二分算法是在题目只要求得长度,这样贪心算法就能最速得到长度,但是始终得不到序列。要得到指定序列还是需要动态规划。可以说是套着dp的皮,实则是贪心策略。","tags":[{"name":"Algorithms","slug":"Algorithms","permalink":"https://zhangziquan.github.io/tags/Algorithms/"},{"name":"LeetCode","slug":"LeetCode","permalink":"https://zhangziquan.github.io/tags/LeetCode/"},{"name":"Dynamic Programming","slug":"Dynamic-Programming","permalink":"https://zhangziquan.github.io/tags/Dynamic-Programming/"},{"name":"Greedy","slug":"Greedy","permalink":"https://zhangziquan.github.io/tags/Greedy/"}]},{"title":"[LeetCode] 514. Freedom Trail","date":"2018-11-09T08:52:00.000Z","path":"2018/11/09/LeetCode-514-Freedom-Trail/","text":"514. Freedom Trail week10 难度:Hard 题目链接 题目描述 In the video game Fallout 4, the quest “Road to Freedom” requires players to reach a metal dial called the “Freedom Trail Ring”, and use the dial to spell a specific keyword in order to open the door. Given a string ring, which represents the code engraved on the outer ring and another string key, which represents the keyword needs to be spelled. You need to find the minimum number of steps in order to spell all the characters in the keyword. Initially, the first character of the ring is aligned at 12:00 direction. You need to spell all the characters in the string key one by one by rotating the ring clockwise or anticlockwise to make each character of the string key aligned at 12:00 direction and then by pressing the center button. At the stage of rotating the ring to spell the key character key[i]: You can rotate the ring clockwise or anticlockwise one place, which counts as 1 step. The final purpose of the rotation is to align one of the string ring’s characters at the 12:00 direction, where this character must equal to the character key[i]. If the character key[i] has been aligned at the 12:00 direction, you need to press the center button to spell, which also counts as 1 step. After the pressing, you could begin to spell the next character in the key (next stage), otherwise, you’ve finished all the spelling. Example: example Input: ring = “godding”, key = “gd” Output: 4 Explanation: For the first key character ‘g’, since it is already in place, we just need 1 step to spell this character. For the second key character ‘d’, we need to rotate the ring “godding” anticlockwise by two steps to make it become “ddinggo”. Also, we need 1 more step for spelling. So the final output is 4. Note: Length of both ring and key will be in range 1 to 100. There are only lowercase letters in both strings and might be some duplcate characters in both strings. It’s guaranteed that string key could always be spelled by rotating the string ring. 题目分析 题目看起来很复杂,其实很简单,说的是辐射4中的一个小游戏,即通过旋转按钮将指针指向对应的字符串,从而根据key按顺序来按出所有的字母,这个按钮可以顺时针或者逆时针旋转,要求算出拼出key的最少步数(按下按钮也算一步)。 解题思路 因为旋钮可以左旋或者右旋,所以每一次的旋转过后的指针都有可能不一样,因为旋钮上的字母是可重复的。可以说每一次的最短路径都不一样。因此我们不能简单的认为每一次都取最短路径就行了,因为上一次的选择会影响下一次的进行。 这样看来这道题目明显就是动态规划中的最短路径问题,我们可以计算一下每个状态下的步数,到达下一个状态取上一个状态+到达下一个状态的步数的最小值,类似于最短路径的做法。因为知道一开始指针必定是指向12点钟方向,所以可以从后往前推,f(i,j) = min(f(i,j),abs(j-k) + f(i+1,k)),求得的f(0)即是最小的步数。定义一个二维数组或者二维向量即可保存其状态,其中i为当前已匹配数,j为指针的方向(指向的位置)。 因为每一次都要按一下确认,所以直接在最后加上key的长度。因为key和ring长度不定,建议使用二维向量。 第一次尝试代码 1234567891011121314151617181920212223242526class Solution{ public: int findRotateSteps(string ring, string key) { vector<vector<int>> steps(key.length() + 1, vector<int>(ring.length())); for (int i = key.length() - 1; i >= 0; i--) { char next = key[i]; for (int j = 0; j < ring.length(); j++) { steps[i][j] = INT_MAX; for (int k = 0; k < ring.length(); k++) { if (next == ring[k]) { int dist = abs(j - k); int step = min(dist, (int)ring.length() - dist); steps[i][j] = min(steps[i][j], step + steps[i + 1][k]); } } } } return steps[0][0] + key.length(); }}; 结果反思 第一次测试 经过测试之后,这种方法的确可行,但看了运行结果发现这个算法运行的并不是很快,当ring变得很长时,花费了很多的时间。在逐步调试之后,一个主要的问题就是在很多不必要的状态下进行了计算。比如有些状态始终不会达到的,没有计算的必要,而且这个三重循环就注定了运行时间并不会很短。 思考了一下并参考了别人的方法,觉得还是使用递归来逐步计算比较好,因为左旋和右旋只有2种,但key同一个字母的个数却不止2种,状态还是设定不够到位,应该把指针的位置作为i,匹配数作为j,这样计算时就少了一些分叉。 比如要找i,如果参照第一种做法,就会遍历所有到i的路径,而第二种做法就只会左旋和右旋到不同i的位置,找出最短的,不必到遍历到多余的i。 改良后的代码 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647class Solution{ public: int findRotateSteps(string ring, string key) { steps = vector<vector<int>>(ring.length(), vector<int>(key.length(), INT_MAX)); return rotate(ring, key, 0, 0) + key.length(); } int rotate(string &ring, string &key, int pointer, int pos) { int right = 0, left = 0; if (pos >= key.size()) { return 0; } if (steps[pointer][pos] != INT_MAX) { return steps[pointer][pos]; } int lp = pointer; int rp = pointer; while (ring[lp] != key[pos]) { lp--; left++; if (lp < 0) //判断是否越界。 { lp = ring.size() - 1; } } while (ring[rp] != key[pos]) { rp++; right++; if (rp == ring.size()) //判断是否越界。 { rp = 0; } } left += rotate(ring, key, lp, pos + 1); right += rotate(ring, key, rp, pos + 1); return steps[pointer][pos] = min(left, right); } private: vector<vector<int>> steps;}; 代码分析 使用了递归避免了三重循环,从0,0开始一直匹配,而且每一个分叉只有左旋和右旋的分叉,减少了分叉的数量从而少计算了很多状态。其中有些状态可以被重复利用,就像很多条路若有一个交叉点,那么后面的最短路径的节点也是交叉的,不必再往后算。这又很像之前的frog jump这道题。修改后的算法居然超越了100%!还是第一次。。。 优化后的结果 总结 动态规划有时候感觉并不会这么容易想得到,虽然都知道通常解法是构建状态,确认策略,然后进行顺推或者倒退来得到结果。但不同的问题都有着不同的性质和解法,所以说解决这类问题只能依靠多熟悉这类问题,多优化多修改,第一次往往都得不到最佳的解法的。","tags":[{"name":"Algorithms","slug":"Algorithms","permalink":"https://zhangziquan.github.io/tags/Algorithms/"},{"name":"LeetCode","slug":"LeetCode","permalink":"https://zhangziquan.github.io/tags/LeetCode/"},{"name":"Dynamic Programming","slug":"Dynamic-Programming","permalink":"https://zhangziquan.github.io/tags/Dynamic-Programming/"}]},{"title":"[LeetCode] 828. Unique Letter String","date":"2018-11-05T12:39:00.000Z","path":"2018/11/05/LeetCode-828-Unique-Letter-String/","text":"828. Unique Letter String week9 难度:Hard 题目链接 题目描述 A character is unique in string S if it occurs exactly once in it. For example, in string S = "LETTER", the only unique characters are "L" and "R". Let’s define UNIQ(S) as the number of unique characters in string S. For example, UNIQ("LETTER") = 2. Given a string S with only uppercases, calculate the sum of UNIQ(substring) over all non-empty substrings of S. If there are two or more equal substrings at different positions in S, we consider them different. Since the answer can be very large, return the answer modulo 10 ^ 9 + 7. Example1: Input: "ABC" Output: 10 Explanation: All possible substrings are: "A","B","C","AB","BC" and "ABC". Evey substring is composed with only unique letters. Sum of lengths of all substring is 1 + 1 + 1 + 2 + 2 + 3 = 10 Example2: Input: "ABA" Output: 8 Explanation: The same as example 1, except uni("ABA") = 1. Note: 0 <= S.length <= 10000. 题目分析 题意是找出一个字符串中的所有子串,再在每个子串中统计所有的只出现一次的字母的个数,最后统计所有的次数之和进行输出。主要是如何找出所有子串且要计算单独出现的字母个数。 解题思路 如果是按照题目的那种例子来算,算出所有的子串,再判断,那么一定非常复杂,不说子串的数量非常多,判断也是一个大问题,最后还要进行统计,这种方法是不可行的,特别是题目给出的字符串最长有1w个字符。 既然正向方法不可行,那么可以尝试使用构造的方法,即根据字符串中的每一个字母特殊构造出一个字符串,若该字符串属于字串,则符合+1,因此题目也就转变成了每一个字母包含其的子串有多少个。 因为有条件限制,且字母的位置固定,因此可以很轻松的判断包含某个字母的子串有多少个,例如A***A**A,这种,若要判断包含中间的A有多少子串,因为有条件限制不能超过1个A,所以就限制在了***A**之中选择,根据排列组合,左边有4种选择,右边有3种选择,所以子串为3*4 = 12。 因此得到方法:将字母左边的其它字母数*右边其它字母数即为子串数目。 代码 1234567891011121314151617181920212223242526272829class Solution{ public: int uniqueLetterString(string S) { vector<int> letter[26]; for (int i = 0; i < 26; i++) { letter[i].push_back(-1); } for (int i = 0; i < S.length(); i++) { letter[S[i] - 'A'].push_back(i); } for (int i = 0; i < 26; i++) { letter[i].push_back(S.length()); } int result = 0; for (int i = 0; i < 26; i++) { for (int j = 1; j < letter[i].size() - 1; j++) { result += (letter[i][j + 1] - letter[i][j]) * (letter[i][j] - letter[i][j - 1]); } } return result % 1000000007; }}; Note 考虑到有可能在字符串的边界这种情况,因此将边界考虑在内进行计算,从而更加方便进行计算而无需反复判断,和二分法类似。 使用了vector数组将不同字母分开,因为这个计算最关键的是找到两边的同种字母和边界来计算出其子串,所以先记录位置再分开计算比较好。 Unique Letter String_test 最佳解法 参考代码 12345678910111213int uniqueLetterString(string S) { int index[26][2], res = 0, N = S.length(), mod = pow(10, 9) + 7; memset(index, -1, sizeof(int) * 52); for (int i = 0; i < N; ++i) { int c = S[i] - 'A'; res = (res + (i - index[c][1]) * (index[c][1] - index[c][0]) % mod) % mod; index[c][0] = index[c][1]; index[c][1] = i; } for (int c = 0; c < 26; ++c) res = (res + (N - index[c][1]) * (index[c][1] - index[c][0]) % mod) % mod; return res;} 代码分析 在网上的讨论中发现了一个更加快的代码,这个思路和上面的思路相似,都是计算子串数,不过这个用了边记录边算方法,因为我们每次算只需要知道目标位置以及两边的边界即可,这个就是利用了这一点只记录了上两次同字母的位置,加上第三次就可计算中间的子串数。不过这个做法没有算完,要在最后的时候再加上包含最后字母的子串数。 这种方法重复利用了空间,减少了遍历的次数,很值得学习。可能有时候这点时间就能决定能不能AC。详细的思路分析 总结 这道题主要是锻炼我们思考问题时的转化能力,怎么样把问题转化成简单的问题,逆向思路解决。另外如何计算包含某特定位置的子串也是一种值得思考的地方。","tags":[{"name":"Algorithms","slug":"Algorithms","permalink":"https://zhangziquan.github.io/tags/Algorithms/"},{"name":"LeetCode","slug":"LeetCode","permalink":"https://zhangziquan.github.io/tags/LeetCode/"}]},{"title":"区块链热身报告","date":"2018-11-04T13:17:00.000Z","path":"2018/11/04/区块链热身报告/","text":"区块链热身报告 16340296 张子权 以太坊的安装、私有链创世区块搭建、私有链节点的加入(选做) 安装以太坊 从http://ethereum.github.io/go-ethereum/downloads/ 下载安装Geth,可能需要在windows下添加环境变量。 私有链创世区块搭建 新建一个genesis.json,设置创世区块的难度,id等参数。新建文件夹data。 初始化创世区块: 1geth --datadir data/ init genesis.json 启动节点并进入交互式控制台: 1geth --datadir data --networkid 1008 --ipcdisable --port 1001 --rpcport 8001 --verbosity=4 console 2>>output.log 私有链节点的加入 再新建一个节点为data0。 启动节点并进入交互式控制台: 1geth --datadir data0 --networkid 1008 --ipcdisable --port 1003 --rpcport 80222 console 在第一个节点中使用admin.nodeInfo.enode获取enode。 在第二个节点中添加第一个节点。 1admin.addPeer(\"enode://e9e7ad186217fbf7b7d7185695dcd17afa73d23e1a1365dd96d01327aee87b2b95af8ca3857ce6b1f6a7d26c58abc19f5e0846787a1d35e21cab7c5fea0cee2a@[::]:1001\") 再使用admin.peers可得到加入节点的信息。 1234567891011121314151617181920[{ caps: [\"eth/62\", \"eth/63\"], enode: \"enode://e9e7ad186217fbf7b7d7185695dcd17afa73d23e1a1365dd96d01327aee87b2b95af8ca3857ce6b1f6a7d26c58abc19f5e0846787a1d35e21cab7c5fea0cee2a@[::]:1001\", id: \"e4a6cb32cfb6b641dd2606bdb67571f63ff80ff1fe4883c1aa46cd5b89e110eb\", name: \"Geth/v1.8.17-stable-8bbe7207/windows-amd64/go1.11.1\", network: { inbound: false, localAddress: \"127.0.0.1:18752\", remoteAddress: \"127.0.0.1:1001\", static: true, trusted: false }, protocols: { eth: { difficulty: 85380105, head: \"0xd2c073c35fb027d8dd8fe4d95174bc82ca93d96599dfb05e8deee320631621f0\", version: 63 } }}] 对 getBlock 中所得区块的各个字段进行解释 字段 类型 意义 difficulty BigNumber 表示当前区块的难度,根据前一个区块的难度和时间戳计算得到,可知当前区块1难度为131072 extraData String 指的是附加信息,可以根据自己的需要填写信息。32字节以内的字节数组。 gasLimit Number 表示当前区块允许使用的最大gas,指这笔交易最多只能给矿工这么多gas,若用不完则返回多出的gas返回,但如果gaslimit不足以支付交易,那么不会退回,因为以太坊规定每笔交易gas最少为21000,所以一定要大于21000。同理若合约的gaslimit小于区块的gaslimit则部署不了。 gasUsed Number 和gasLimit相对,为当前区块所累积使用的gas值。 hash String 指该区块的哈希值,当区块等待被区块链确认时,则为null,可以认为是区块在整个区块链的索引。 logsBloom String 日志Bloom过滤器,信息包含在每个日志入口, 来自交易列表中每个交易的接受者。 miner String 表示打包这一区块的矿工的地址。 mixHash String 混合哈希,与nonce进行哈希运算,证明已经付出了足够的工作量。 nonce String 一个随机数64位哈希,表示该矿工找到的符合条件的nonce值。 number Number 为区块编号,指该区块为第几个区块。 parentHash String 父哈希,指上一个区块的哈希值。因为创世区块没父区块,所以这个值为0。 receiptsRoot String 交易完成后,由交易收据,日志内容构成的数据的字典树根节点哈希。 sha3Uncles String 类似上一个,指由叔区块哈希构成的字典树的根节点哈希。 size Number 当前这个区块的字节大小。 stateRoot String 状态字典树根节点的哈希。 timestamp Number 指当前该区块初始化时的Unix时间戳。 totalDifficulty Number 到当前区块为止,所积累的所有区块难度之和。 transactions 数组 当前区块所包含的交易。 transactionsRoot String 包含所有交易的交易字典树的根节点哈希值。 Uncles 数组 包含当前区块的所以叔区块。 对日志输出进行解释 初始化区块链,分配文件夹和空间,写入创世块,持久化字典树。 1234567INFO [11-04|15:30:01.915] Allocated cache and file handles database=\"D:\\\\Program Files\\\\Geth\\\\data\\\\geth\\\\chaindata\" cache=16 handles=16INFO [11-04|15:30:01.940] Writing custom genesis blockINFO [11-04|15:30:01.941] Persisted trie from memory database nodes=0 size=0.00B time=0s gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00BINFO [11-04|15:30:01.941] Successfully wrote genesis state database=chaindata hash=5e1fc7…790e0 开始连接时,初始化以太坊协议,载入最近的区块,重新加载交易和生成交易日志。 12345678910INFO [11-04|15:31:05.759] Initialising Ethereum protocol versions=\"[63 62]\" network=1008INFO [11-04|15:31:05.759] Loaded most recent local header number=0 hash=5e1fc7…790e0 td=131072 age=49y6mo2wINFO [11-04|15:31:05.760] Loaded most recent local full block number=0 hash=5e1fc7…790e0 td=131072 age=49y6mo2wINFO [11-04|15:31:05.760] Loaded most recent local fast block number=0 hash=5e1fc7…790e0 td=131072 age=49y6mo2wDEBUG[11-04|15:31:05.760] Reinjecting stale transactions count=0INFO [11-04|15:31:05.762] Regenerated local transaction journal transactions=0 accounts=0 输入exit关闭连接,之后将缓存中的状态写入磁盘持久化数据,关闭区块链,关闭协议和交易池,最后关闭数据库。 123456789101112131415INFO [11-04|17:11:41.533] Writing cached state to disk block=454 hash=4713ec鈥?7d4ca root=a006ff鈥e94eaINFO [11-04|17:11:41.533] Persisted trie from memory database nodes=0 size=0.00B time=0s gcnodes=0 gcsize=0.00B gc time=0s livenodes=1 livesize=0.00BINFO [11-04|17:11:41.533] Blockchain manager stoppedINFO [11-04|17:11:41.533] Stopping Ethereum protocolINFO [11-04|17:11:41.533] Ethereum protocol stoppedINFO [11-04|17:11:41.533] Transaction pool stoppedINFO [11-04|17:11:41.533] Database closed database=\"D:\\\\Program Files\\\\Geth\\\\data\\\\geth\\\\chaindata\" 矿工挖矿,打包新区块,开采潜在的区块。 123INFO [11-04|17:30:48.082] Commit new mining work number=593 sealhash=108a77…bbb5d6 uncles=0 txs=0 gas=0 fees=0 elapsed=35.904msINFO [11-04|17:30:49.212] Successfully sealed new block number=593 sealhash=108a77…bbb5d6 hash=c52790…332920 elapsed=1.166sINFO [11-04|17:30:49.217] 🔨 mined potential block number=593 hash=c52790…332920 提交交易。 123INFO [11-04|18:25:09.342] Setting new local account address=0x15b97213fB8ee04D2ed53113E41035FEaE88461eINFO [11-04|18:25:09.346] Submitted transaction fullhash=0x889aa5bb7bc45463a5f59012b2ac15abdce71542a48bc6a6be91969c48eac6a5 recipient=0x6a949Cd94E739d035d13c2242B32093553567361\"0x889aa5bb7bc45463a5f59012b2ac15abdce71542a48bc6a6be91969c48eac6a5\" 随后通过挖矿发现有一gas=420000的区块。 1INFO [11-04|18:32:42.760] Commit new mining work number=607 sealhash=916dea…38331b uncles=0 txs=2 gas=42000 fees=4.2e-05 elapsed=8.943ms 添加节点。 12INFO [11-04|18:39:56.578] Block synchronisation startedINFO [11-04|18:39:56.709] Imported new chain segment blocks=26 txs=7 mgas=0.126 elapsed=111.730ms mgasps=1.128 number=607 hash=d2c073…1621f0 age=7m14s cache=17.84kB ignored=2 编写简单的智能合约,在 remix 下进行调试,并部署在链上进行调用 编写简单的智能合约 编写一个类似与复读机的东西,发送字符串保存,之后复读发送的话。 solidity代码: 12345678910111213141516171819contract AI { string box; function send(string s) public { box = s; } function clear() public{ box = \"\"; } function receive() public constant returns (string) { return box; } function greet() public constant returns (string) { return \"hello\"; }} 进行调试部署调用 部署合约 进行挖矿后成功部署。 greet 发送信息,产生交易,挖矿后交易成功。 交易成功后更新里面的string,再用receive得到刚刚的消息。 清除消息,也需要产生交易,然后挖矿使交易成功。 对交易的字段进行解释 根据以上3的发送消息的交易进行解释。 1234567891011121314151617eth.getTransaction(\"0xeb7a55e1760ecc986f813c881b76ab12b812bc611f8f6b9cacb69248438b50ec\"){ blockHash: \"0x06258165d31d4ea7ed945fa430d8e25f75690c0ae20ca4d2ae8f6de40576d261\", blockNumber: 642, from: \"0x15b97213fb8ee04d2ed53113e41035feae88461e\", gas: 43406, gasPrice: 1000000000, hash: \"0xeb7a55e1760ecc986f813c881b76ab12b812bc611f8f6b9cacb69248438b50ec\", input: \"0x66792ba10000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000ce5bc80e5a78be5a48de8afbb0000000000000000000000000000000000000000\", nonce: 14, r: \"0x3892abe334fbe797ce57155373c9c36860121d143950f873e554dc37321847d\", s: \"0x55eea1e92c492bd89f7db381fda08ae795fdc7abdbcff4c98f897cae94edae7c\", to: \"0x2eb0f4a6c064c51033dbc5977973bcb6702a9cdd\", transactionIndex: 0, v: \"0x37\", value: 0} 字段名称 类型 意义 blockHash String 交易所处区块的哈希值 blockNumber Number 当前交易所处在区块的编号 from String 发起交易的账户(地址) gas Number 完成这笔交易所需要的gas gasPrice Number 当前每个gas所需的花费 hash String 当前交易的哈希值 input String 部署智能合约交易的16进制代码,合约调用相关的二进制信息 nonce Number 相当于发起人的交易序号,每发送一笔交易,nonce+1,放置交易重复进行 r String 发起人EOA的ECDSA签名的三个组成部分 s String 发起人EOA的ECDSA签名的三个组成部分 to String 目标以太坊地址 transactionIndex Number 交易索引 v String 发起人EOA的ECDSA签名的三个组成部分 values Number 要发送到目的地址的以太币数","tags":[{"name":"ethereum","slug":"ethereum","permalink":"https://zhangziquan.github.io/tags/ethereum/"}]},{"title":"[LeetCode] 45. Jump Game II","date":"2018-10-28T13:57:00.000Z","path":"2018/10/28/LeetCode-45-Jump-Game-II/","text":"45. Jump Game II week8 难度:Hard 题目链接 题目描述 Given an array of non-negative integers, you are initially positioned at the first index of the array. Each element in the array represents your maximum jump length at that position. Your goal is to reach the last index in the minimum number of jumps. Note: The number of stones is ≥ 2 and is < 1,100. Each stone’s position will be a non-negative integer < 231. The first stone’s position is always 0. Example: Input: [2,3,1,1,4] Output: 2 Explanation: The minimum number of jumps to reach the last index is 2. Jump 1 step from index 0 to 1, then 3 steps to the last index. Note: You can assume that you can always reach the last index. 题目分析 这道题目十分简单,就是给予一个数组,使得一开始在数组的首元素中,能够跳越到最后一项,而每项中的数字即跳跃的极限范围。给出最快到达数组尾的步数。 解题思路 和上一周所做的青蛙跳十分类似,但又不太一样,因为这次的只给出了跳越的极限,所以范围可以选择0~x,使得几乎都可以到达终点。 既然是这样的话,那么就可以不需要进行分支预测和判断了,直接使用贪心算法,贪心策略并不是每次都走到最远的距离,而是根据到达的顶点之后所能达到的最大距离。这样就能保证每一个的距离都是由上一个的最远距离而来。 代码 1234567891011121314151617181920212223242526272829class Solution {public: int jump(vector<int>& nums) { if(nums.size() == 0 || nums.size() == 1){ return 0; } int maxjump = nums[0]; int steps = 0; int index = 0; for(; index <= maxjump && index < nums.size() - 1;){ steps++; if(maxjump >= nums.size() -1){ return steps; } int nextjump = maxjump; int start = index; for(int i = nextjump; i >= start; i--){ if(i + nums[i] > maxjump){ maxjump = i + nums[i]; index = i; } } } return -1; }}; Note 这个贪心策略的证明可以根据递归来说明,即已经到达了终点,那么要寻找达到终点的最远的起点,不断地往前回溯,最后可以得知每一次判断下一个顶点的最远距离来选择下一个跳点,能够最快到达终点。 这个算法同青蛙跳石头一样可以进行优化,我们可以选择从当前可跳越的最远距离的顶点开始遍历,那么很有可能第一次就得到最佳跳点,就不用经常进行赋值运算。经过优化后的算法可从12ms降为8ms。 Jump Game II_test 总结 最近刚学习贪心算法,恰好用一下,这个题目的关键之处在于怎么去证明贪心是可行的,有可能不一定贪心就可以,就像上一周青蛙跳的题目,使用贪心算法时一定要注意是否满足条件。","tags":[{"name":"Algorithms","slug":"Algorithms","permalink":"https://zhangziquan.github.io/tags/Algorithms/"},{"name":"LeetCode","slug":"LeetCode","permalink":"https://zhangziquan.github.io/tags/LeetCode/"}]},{"title":"[LeetCode] 403. Frog Jump","date":"2018-10-19T08:13:00.000Z","path":"2018/10/19/LeetCode-403-Frog-Jump/","text":"403. Frog Jump week7 难度:Hard 题目链接 题目描述 A frog is crossing a river. The river is divided into x units and at each unit there may or may not exist a stone. The frog can jump on a stone, but it must not jump into the water. Given a list of stones’ positions (in units) in sorted ascending order, determine if the frog is able to cross the river by landing on the last stone. Initially, the frog is on the first stone and assume the first jump must be 1 unit. If the frog’s last jump was k units, then its next jump must be either k - 1, k, or k + 1 units. Note that the frog can only jump in the forward direction. Note: The number of stones is ≥ 2 and is < 1,100. Each stone’s position will be a non-negative integer < 231. The first stone’s position is always 0. Example1: [0,1,3,5,6,8,12,17] There are a total of 8 stones. The first stone at the 0th unit, second stone at the 1st unit, third stone at the 3rd unit, and so on... The last stone at the 17th unit. Return true. The frog can jump to the last stone by jumping 1 unit to the 2nd stone, then 2 units to the 3rd stone, then 2 units to the 4th stone, then 3 units to the 6th stone, 4 units to the 7th stone, and 5 units to the 8th stone. Example2: [0,1,2,3,4,8,9,11] Return false. There is no way to jump to the last stone as the gap between the 5th and 6th stone is too large. 题目分析 著名的青蛙跳石头问题,在大一的时候一次ACM比赛中碰到一次,貌似是原题,结果很显然扑街了,现在看到题目才算正式理解了题目。。。想来当时没有学习什么数据结构也是做不出的了,暴力AK明显会timelimit。 青蛙跳石头的题意是有一只青蛙要过河,河流分成X个部分,有的地方有石头,有的没有。而这只青蛙要连续跳过去,可能这只青蛙的手刹有点毛病,每当跳过K个单位时,下一次跳越要是K-1、K或者K+1个单位,并且只能向前。 一开始的理解是只要判断下一块石头是否在跳越范围内就行了,但注意的是青蛙可以飞过某一块石头,所以就有很多种跳越方式,假设有两条路径能到达点A,通过不同路径后到达点A后,其下一步的跳越范围(能力)也会不一样,所以情况会很复杂。 第一次解题思路 由于青蛙的选择路径的多变,会引起后续的跳越,因此它的选择分支是从第一次就开始了,试想一下每个分支又会衍生不同的分支,那不就是一颗路径树了吗,只要遍历这一棵树,找到某一路径可以到达终点即可,因为只需要得出青蛙是否能过河的结论,所以无需遍历所有路径(除非所有路径都不能到达)。 为了尽快地解决问题,很明显是用深度遍历的,当得到一条路径后,马上返回true节省时间。所以使用递归的方式,遍历所有状态。 代码 123456789101112131415161718192021222324252627282930313233#include<vector>#include<iostream>#include <algorithm>using namespace std;class Solution {public: bool canCross(vector<int>& stones) { stoness = stones; return cross(stoness.begin(),0); } bool cross(vector<int>::iterator start, int dis) { if(start == stoness.end()-1) { return true; } for(int i = dis - 1; i <= dis+1; i++) { vector<int>::iterator it = find(start+1,stoness.end(),i+*start); if(it != stoness.end()){ if(cross(it,i)) { return true; } } else { continue; } } return false; }private: vector<int>stoness;}; 结果与反思 在过了example后,提交的检测超时了,例子是[1…998,99999999],这个极端的例子展示了这个算法的弱点,没有根据搜索得的信息加快速度,每次都搜索到了998这个节点,但由于最后一个点不可到达,所以都在最后一步终止,浪费了大量的时间,所以我们要采取一种措施及时止损。 第二次解题思路 采用遍历路径树是没有错的,因为我们需要验证每一条路径是否能通往最后一个节点,但我们不能重复判断同一个状态多次,因为每条路径中有着许多相同的状态,即达到点相同,且下一步的跳越范围相同,如果我们在一条路径中已经得知该状态是否能达到终点,就可以在其它路径中引用进行快速判断。就是如一个节点不通,则经过该节点的所有路径都不通。 具体方法是维护一个哈希表保存状态的可行性,因为一个状态有两个属性,一是点的位置,二是上一步跳越距离,因为石头数量有限,可以通过左移取或,将两个值合并起来得到一个唯一的值,从而确定一个唯一的状态,保存该状态是否能达到终点。 改进代码 123456789101112131415161718192021222324252627282930313233343536373839#include<vector>#include<iostream>#include <algorithm>#include<unordered_map>using namespace std;class Solution {public: bool canCross(vector<int>& stones) { stoness = stones; return cross(stoness.begin(),0); } bool cross(vector<int>::iterator start, int dis) { if(start == stoness.end()-1) { return true; } int key = *start | dis<<11; if (status.count(key)) { return status[key]; } for(int i = dis + 1; i >= dis-1; i--) { vector<int>::iterator it = find(start+1,stoness.end(),i+*start); if(it != stoness.end()){ if(cross(it,i)) { return status[key] = true; } } else { continue; } } return status[key] = false;; }private: vector<int>stoness; unordered_map<int, bool> status;}; Note 在查询下一个跳越点时,可以使用循环来寻找,但我觉得如果跳越距离很大,那么就要遍历很多个位置,因此使用find直接在vector中寻找对应k-1,k,k+1距离的点会更加快。 在看了一下别人的做法中,发现可以先在两个点之后,判断是否后一个点是前一个点的两倍还多,即stone[i]>2*stone[i-1],若存在这一情况,那么必定不能到达,因为青蛙在2次跳越后,不可能跳越2倍的距离,而且这个数组又是升序的,所以不可逾越。可以去掉很多极端情况。 看了一下题目的评论,有一个评论说从步数大的开始遍历会更快得到答案,想了一下似乎是这样的,因位较小的步数最终很有可能走到大步数的状态,中间多出了很多不必要的状态,改成i–后,从24ms提升到了16ms,感觉改善很客观。居然能到95.46%了。 Frog Jump_test 总结 这个题目的关键之处在于如何利用已知的信息进行搜索,而不是采用盲目搜索的方式,在搜索过程中,发现一个点不通则将之后的路径封掉,对信息的复用,极大加快算法的速度,另外一些顺序的选择也很有可能影响速度。","tags":[{"name":"Algorithms","slug":"Algorithms","permalink":"https://zhangziquan.github.io/tags/Algorithms/"},{"name":"LeetCode","slug":"LeetCode","permalink":"https://zhangziquan.github.io/tags/LeetCode/"}]},{"title":"[LeetCode] 297. Serialize and Deserialize Binary Tree","date":"2018-10-14T08:38:00.000Z","path":"2018/10/14/LeetCode-297-Serialize-and-Deserialize-Binary-Tree/","text":"297. Serialize and Deserialize Binary Tree week6 难度:Hard 题目链接 题目描述 Serialization is the process of converting a data structure or object into a sequence of bits so that it can be stored in a file or memory buffer, or transmitted across a network connection link to be reconstructed later in the same or another computer environment. Design an algorithm to serialize and deserialize a binary tree. There is no restriction on how your serialization/deserialization algorithm should work. You just need to ensure that a binary tree can be serialized to a string and this string can be deserialized to the original tree structure. Example: You may serialize the following tree: 1 / \\ 2 3 / \\ 4 5 as \"[1,2,3,null,null,4,5]\" Clarification: The above format is the same as how LeetCode serializes a binary tree. You do not necessarily need to follow this format, so please be creative and come up with different approaches yourself. Note: Do not use class member/global/static variables to store states. Your serialize and deserialize algorithms should be stateless. 题目分析 题目的意思是将一棵树序列化为字符串,而且序列化后的树能够经过反序列化变为原来的树结构。序列化的意思是将状态信息转化为可以存储和传输的形式,这在很多场合都能够用到,可以使自定义对象持久化,方便传输对象,以及便于程序维护等等。 解题思路 题目已经给出了LeetCode所用的二叉树的序列化的形式,即将二叉树的每个节点的值存在一个字符串中,用标点符号隔开。观察其顺序可以发现是使用分层方式来构造的,因此我们可以用分层遍历的方式来构造这个字符串,然后再还原。 使用分层遍历,构造一个队列存储遍历的节点并将子节点放在队列中,从而逐层遍历,构造字符串,注意也要把空节点放进去,否则将无法还原为原来的树。 得到字符串并以“,”为分割点分割成不同的字符串节点,然后使用队列构造回原来的树。 代码 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */#include <string>#include <queue>using namespace std;class Codec {public: // Encodes a tree to a single string. string serialize(TreeNode* root) { string sertree; queue<TreeNode*> queue; queue.push(root); while(!queue.empty()){ TreeNode* node = queue.front(); queue.pop(); if(sertree.length()>0) { sertree += \",\"; } if(node == nullptr) { sertree += \"null\"; } else { sertree += std::to_string(node->val); queue.push(node->left); queue.push(node->right); } } return sertree; } // Decodes your encoded data to tree. TreeNode* deserialize(string data) { if(data == \"null\") { return nullptr; } vector<string> treeNodes = split(data,\",\"); TreeNode* root = new TreeNode(atoi(treeNodes[0].c_str())); queue<TreeNode*> queue; queue.push(root); for(int i = 1; i < treeNodes.size(); i+=2){ TreeNode* node = queue.front(); queue.pop(); if(treeNodes[i] != \"null\"){ TreeNode* left = new TreeNode(atoi(treeNodes[i].c_str())); node->left = left; queue.push(left); } if(treeNodes[i+1] != \"null\"){ TreeNode* right = new TreeNode(atoi(treeNodes[i+1].c_str())); node->right = right; queue.push(right); } } return root; } vector<string> split(const string& str, const string& delim) { vector<string> res; if(\"\" == str) return res; //先将要切割的字符串从string类型转换为char*类型 char * strs = new char[str.length() + 1] ; strcpy(strs, str.c_str()); char * d = new char[delim.length() + 1]; strcpy(d, delim.c_str()); char *p = strtok(strs, d); while(p) { string s = p; //分割得到的字符串转换为string类型 res.push_back(s); //存入结果数组 p = strtok(NULL, d); } return res; }};// Your Codec object will be instantiated and called as such:// Codec codec;// codec.deserialize(codec.serialize(root)); Note 注意c中的string中没有split这个函数,所以要自己写一个split函数,使用c里的strtok方法,先将字符串转化成Char数组类型,(注意char*最后有一个终止符,length+1),然后调用这个方法来处理char数组,通过循环进行逐步分割,再变成string,最后传出结果vector; 反序列化为树结构时,需要注意要去除空节点。否则将会出现访问空节点的错误,另外因为存放了null,所以每一次可以同时访问左右两个节点。 除了使用分层序列化之外,还有许多方法,比如使用深度+递归,广度遍历也可以,只要能够做到反序列化从转化为原来的树结构即可。 总结 在这个题目中,我发现JAVA和C其实很大区别的,写多了JAVA之后,发现许多方法都在c没有提供,比如split,还有许多很方便使用的Likedlist等等数据结构。所以在JAVA中许多包装的方法还是挺好用的,以前还觉得很相似,但是现在发现在应用上的便利性上差距挺大的,所以遇到问题可以先使用JAVA来完成一下,确定解决的方法,然后再使用C++来实现。 看了一下除此之外有人使用map<long,TreeNode*>来进行存储,直接偷鸡使用全局变量存储状态。。。不算是序列化。而且题目已经标明了不可用存储状态的做法了。","tags":[{"name":"Algorithms","slug":"Algorithms","permalink":"https://zhangziquan.github.io/tags/Algorithms/"},{"name":"LeetCode","slug":"LeetCode","permalink":"https://zhangziquan.github.io/tags/LeetCode/"}]},{"title":"[LeetCode] 679. 24 Game","date":"2018-10-07T12:49:00.000Z","path":"2018/10/07/LeetCode-679-24-Game/","text":"24 Game week5 难度:Hard 题目链接 题目描述 You have 4 cards each containing a number from 1 to 9. You need to judge whether they could operated through *, /, +, -, (, ) to get the value of 24. Example1: Input: [4, 1, 8, 7] Output: True Explanation: (8-4) * (7-1) = 24 Example2: Input: [1, 2, 1, 2] Output: False Note: 1.The division operator / represents real division, not integer division. For example, 4 / (1 - 2/3) = 12. 2. Every operation done is between two numbers. In particular, we cannot use - as a unary operator. For example, with [1, 1, 1, 1] as input, the expression -1 - 1 - 1 - 1 is not allowed. 3. You cannot concatenate numbers together. For example, if the input is [1, 2, 1, 2], we cannot write this as 12 + 12. 题目分析 这道题目的意思很明显,就是要解决24点的问题,随机给出4个数,仅能用四则运算,若得到24点则输出正确,否则输出错误。 解题思路 解决这道题一开始是使用穷举法,将所有的可能都列举出来,然后进行判断,但每一次运算只能减少一个数,而一开始有4个数,4个运算法则,而四则运算中,乘和加是和两个数顺序无关的,那么结果有$$C2_4*6*C2_36C^2_2*6 = 3888$$ 种可能,有点多,不可能用代码都列出来。 所以接下来考虑利用递归的方式,将每一种方法都遍历一遍,类似于深度搜索的方法,构成一棵树,根为4个数,每个分支代表一次运算,其子节点数的个数-1,因此树的高度为4,用深度遍历的原因是我们只需要把这个24点有无解输出就可以了,不需要输出解甚至所有的解,所以每次遍历都要达到高度4,以便最快得到一个解。 采用递归的方式能够更好的遍历,当一个解不符合时,马上退回上一个状态,寻找下一个解,这里使用vector存储得到的解,当不符合时,弹出得到的运算数,回到上一状态。 代码 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172class Solution {public: bool judgePoint24(vector<int>& nums) { vector<double> dnums; for(int val:nums) { dnums.push_back((double)val); } return dfs(dnums); } bool dfs(vector<double>& nums) { if(nums.size() == 0) { return false; } else if(nums.size() == 1) { return abs(nums[0] - 24) < 1e-6; } int size = nums.size(); for(int i = 0; i < size; i++) { for(int j = 0; j < size; j++) { if(i == j) { continue; } vector<double>card; for(int k = 0;k < size; k++) { if(k != i && k != j) { card.push_back(nums[k]); } } for(int l = 0; l < 4; l++) { if(l == 0) { card.push_back(nums[i]+nums[j]); } else if(l == 1) { card.push_back(nums[i]*nums[j]); } else if(l == 2) { card.push_back(nums[i]-nums[j]); } else if(l == 3 && nums[j]!= 0) { card.push_back(nums[i]/nums[j]); } if(dfs(card)) { return true; } card.pop_back(); } } } return false; }}; Note 因为所给的数的类型是int,因此在运算时要将其转成double类型方便进行乘除运算,否则会出现较大误差,并且判断是否为24点时因计算机的运算可能存在误差,所以要根据其和24点的误差小于一定值来判断。 因为在加减乘除中,加和乘对于数作为被加/乘数和加/乘数的结果是无影响的,因此,可以跳过2次运算,这样相当于每个节点原本8个子节点缩减为6个子节点,大大减少其分支,一开始以为没少多少,但是由于树高为4,算出的解能少3/4,极大减少时间。 加减乘除的顺序可以调转,也可以是先做加运算一直做到头,再做其它运算,也是可以的,但是个人觉得运算混搭可能会加快得到解的速度,即得到解的概率会大一些,单一进行运算很难得到24点。又由于其运算结果个数并不多,所以感觉使用启发式搜索并没有太大改进,判断时间可能使搜索时间变得更多。 总结 思路主要是来源于题目写了一个tag为“depth-first search”让我有了一点思路,做这一些判断等题目,若是要进行所有结果的遍历,那么最好的方法就是进行搜索,搜索主要就是用到了树的遍历,即遍历状态。如果状态数过多,那么使用启发式搜索要比盲目搜索更好,而盲目搜索也要根据目的来确定,启发式搜索的难点就在于调参,确定一个搜索方向,而这道题因方向难以确定,而状态数不多,因此才采用深度搜索。","tags":[{"name":"Algorithms","slug":"Algorithms","permalink":"https://zhangziquan.github.io/tags/Algorithms/"},{"name":"LeetCode","slug":"LeetCode","permalink":"https://zhangziquan.github.io/tags/LeetCode/"}]},{"title":"[LeetCode] 124. Binary Tree Maximum Path Sum","date":"2018-09-30T02:17:00.000Z","path":"2018/09/30/LeetCode-124-Binary-Tree-Maximum-Path-Sum/","text":"Binary Tree Maximum Path Sum week4 难度:Hard 题目链接 题目描述 Given a non-empty binary tree, find the maximum path sum. For this problem, a path is defined as any sequence of nodes from some starting node to any node in the tree along the parent-child connections. The path must contain at least one node and does not need to go through the root. Example1: Input: [1,2,3] 1 / \\ 2 3 Output: 6 Example2: Input: [-10,9,20,null,null,15,7] -10 / \\ 9 20 / \\ 15 7 Output: 42 题目分析 这道题目要求找出一颗二叉树中的得到最大数之和的一条路径,路径中的头和尾可以是二叉树中的任意一节点。这也就是说除了可以经过根节点外,还可以是二叉树里任意子树,只要得到的和为最大。 解题思路 首先想到的是用递归的算法,来算出每一个节点作为根节点所得到的最大路径和,这和书上第四章的某道题目有点相像:已知两顶点,求出两个顶点存在多少条路径。当时我们可以使用递归方式分别求出上一层到下一层节点有多少条路径,逐步计算出到达每一层的路径,最后得到总的路径数。 这道题目也是这样,子树的最大路径和可以是左子树+根+右子树等等,但其作为上一层的子节点只能是左子树/右子树+根。因此我们判断长度和返回到上一层的值是不一样的。即maxNum = max(leftNum + rightNum + node->val,maxNum);(若左右子树<0,则设其为0)和返回的值return node->val + sumNum; 代码 12345678910111213141516171819202122232425262728293031323334353637383940414243/* * * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */#include <algorithm>class Solution {public: int maxNum; int maxPathSum(TreeNode* root) { if(root != nullptr) { maxNum = root->val; } sumPath(root); return maxNum; } int sumPath(TreeNode * node) { if(node == nullptr) { return 0; } int leftNum = sumPath(node->left);//求左右子节点的最大路径和 int rightNum = sumPath(node->right); if(leftNum < 0) { leftNum = 0; } if(rightNum < 0) { rightNum = 0; } int sumNum = max(leftNum,rightNum); maxNum = max(leftNum + rightNum + node->val,maxNum);//每一次和原本的值作比较 return node->val + sumNum; }}; Note 有可能存在只有一个节点,所以一开始最大和设为根节点的值,也可以设置成INT_MIN,因为可能最终的值是负数。 因为不一定经过根节点的就是路径最大和,而且每一颗子树都有可能是目标树或者一个特别大的节点,所以每次递归都要进行一次比较,以求得最大的值。 做完之后发现代码运行好像很慢,比较了一下和大佬的代码,发现还是写的太复杂了一点,有一些判断根本是不必要的,比如判断是否<0等,直接用max取最大的就可以了,因为负数不是不合法的,不会产生什么逻辑错误。 此外发现大佬的代码中写了这么一句速度看起来比我的快不少。。。在网上查了下之后似乎是cin、cout效率低的原因是要把东西输入到缓冲区在进行输入输出,这一句语句可以取消其缓冲,使得和scanf、printf的效率差不多。。这也解释了为什么以前有时候用cin和cout会超时,而用scanf、printf不会。 1static int fast = []() {ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0); return 0; }(); 附上大佬简洁的代码。。但感觉阅读上可能并不会很快,可能有时候需要在简洁和易懂中作出取舍 12345678910111213141516static int fast = []() {ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0); return 0; }();class Solution {public: int go(TreeNode* t, int& Ans) { //maxPathSum through t if (!t) return 0; int L = go(t->left, Ans), R = go(t->right, Ans), Ret = 0; Ret = max(t->val, t->val + max(L, R)); Ans = max(Ans, max(L + R + t->val, Ret)); return Ret; } int maxPathSum(TreeNode* t) { int Ans = INT_MIN; go(t, Ans); return Ans; }};","tags":[{"name":"Algorithms","slug":"Algorithms","permalink":"https://zhangziquan.github.io/tags/Algorithms/"},{"name":"LeetCode","slug":"LeetCode","permalink":"https://zhangziquan.github.io/tags/LeetCode/"}]},{"title":"[LeetCode] 145. Binary Tree Postorder Traversal","date":"2018-09-23T02:10:00.000Z","path":"2018/09/23/LeetCode-145-Binary-Tree-Postorder-Traversal/","text":"Binary Tree Postorder Traversal week3 难度:Hard 题目链接 题目描述 Given a binary tree, return the postorder traversal of its nodes’ values. Example: Input: [1,null,2,3] 1 \\ 2 / 3 Output: [3,2,1] Follow up: Recursive solution is trivial, could you do it iteratively? 解题思路 这道题目就是普通的后序遍历,即访问一棵树对其子节点使用左,右,中的顺序进行访问,其中有递归和遍历的做法。 递归思路: 递归的做法就是采用分而治之的方法,对一棵树均划分成3个节点,每当到一个节点时,先对其左节点递归,再到右节点,最后插入中节点后结束递归,以及当所到节点为空时也结束递归。 遍历思路: 遍历的思路就是使用一个栈来存取遍历的节点,当访问完其中所有的子节点时,再使它出栈,其中也是用了递归的思路,因为递归就是通过栈来实现的。 代码 递归思路 12345678910111213141516171819202122232425/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */class Solution {public: vector<int> postorderTraversal(TreeNode* root) { vector <int> ans; if(root == nullptr) { return ans; } vector <int> leftans = postorderTraversal(root->left); vector <int> rightans = postorderTraversal(root->right); ans.insert(ans.end(),leftans.begin(),leftans.end()); ans.insert(ans.end(),rightans.begin(),rightans.end()); ans.insert(ans.end(),root->val); return ans; }}; 遍历思路 123456789101112131415161718192021222324252627/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */class Solution {public: vector<int> postorderTraversal(TreeNode* root) { vector<int> res; if (!root) return res; stack<TreeNode*> s; s.push(root); TreeNode* pre = root; while (!s.empty()) { TreeNode* p = s.top(); s.pop(); res.insert(res.begin(), p->val); if (p->left) s.push(p->left); if (p->right) s.push(p->right); } return res; }}; Note 按照时间复杂度来算遍历的应该比递归的要快,但在网页上显示是一样的速度,看了一下比较慢的就是一些人先是用了栈来处理节点,到最后访问子节点的时候又用回递归的方法了,这种是典型的没有理解好stack的用法,其先进后出的性质可以让子节点也使用stack来进行遍历。 此外还有一种做法就是使用先序遍历后,再进行翻转从而得到后序遍历的结果。 1234567891011121314151617stack<TreeNode*> stack;stack.push(root);while (!stack.empty()) { TreeNode* cur = stack.top(); stack.pop(); res.push_back(cur->val); if (cur->left != nullptr) { stack.push(cur->left); } if (cur->right != nullptr) { stack.push(cur->right); }}reverse(res.begin(),res.end());return res;","tags":[{"name":"Algorithms","slug":"Algorithms","permalink":"https://zhangziquan.github.io/tags/Algorithms/"},{"name":"LeetCode","slug":"LeetCode","permalink":"https://zhangziquan.github.io/tags/LeetCode/"}]},{"title":"[LeetCode] 765. Couples Holding Hands","date":"2018-09-15T09:11:00.000Z","path":"2018/09/15/LeetCode/","text":"Couples Holding Hands week 2 题目链接 题目描述 N couples sit in 2N seats arranged in a row and want to hold hands. We want to know the minimum number of swaps so that every couple is sitting side by side. A swap consists of choosing any two people, then they stand up and switch seats. The people and seats are represented by an integer from 0 to 2N-1, the couples are numbered in order, the first couple being (0, 1), the second couple being (2, 3), and so on with the last couple being (2N-2, 2N-1). The couples’ initial seating is given by row[i] being the value of the person who is initially sitting in the i-th seat. Example 1: Input: row = [0, 2, 1, 3] Output: 1 Explanation: We only need to swap the second (row[1]) and third (row[2]) person. Example 2: Input: row = [3, 2, 0, 1] Output: 0 Explanation: All couples are already seated side by side. Note: len(row) is even and in the range of [4, 60].row is guaranteed to be a permutation of 0…len(row)-1. 题目分析 这个题意为有N对情侣坐在2N个座位上,但是他们并不是坐在一起的,要使得每一对都能并肩坐在一起,计算最少的交换次数,一次交换可以选择任意的两个人。 情侣按照顺序编号,并不是相连的都是情侣,(0,1),(2,3)等等。由此可知一个偶数的下一个奇数必定是它的另一半。 解题思路 这里的交换有两种可能: 交换一次后,有两组情侣完成配对。 交换一次后,仅有一组情侣完成配对,而另一个等待继续交换。 可知如果每一次都是第一种交换的话,则交换次数会比第二种要少,第一种为最佳交换,因此怎么样才能让其先完成第一种匹配呢,我的想法是,既然是第一种,则他们的情侣是交叉坐的,因此若这4个人中有2组情侣,则其和其他组的情侣就没有交集,所以无论如何其它组的情侣如何交换都不会影响到他们。(这里指交换都要匹配成功,如果试图以一次不成功的匹配凑出第一种情况,那么要匹配成功还要交换一次,那么使用第二种也是2次,在次数上是没有任何不同的) 所以我们可以采用贪心算法,遍历每一组,当它的旁边不是它的情侣时,就向后遍历寻找其情侣,进行交换然后匹配成功,这里因为我们发现他们的id的特殊性,可以判断奇偶,快速找到其另一半。 代码 12345678910111213141516171819202122232425262728293031323334class Solution {public: int minSwapsCouples(vector<int>& row) { int ans = 0; for (int i = 0; i < row.size(); i +=2 ) { int cp; if (row[i] % 2 == 0) { cp = row[i] + 1;//若为偶数则情侣为下一个奇数 } else { cp = row[i] - 1;//若为奇数则情侣为上一个偶数 } if (row[i + 1] == cp) { continue; } ans++; for (int j = i + 2; j< row.size(); j++) { if (row[j] == cp) { int temp = row[i+1]; row[i+1] = row[j]; row[j] = temp; break; } } } return ans; }}; 总结 当做到这道题目的时候,便想使用贪心算法,但一直都想不清楚如何去证明,后来发现其实他们的交换先后是无关的。 根据网上严谨的证明应该是把row抽象成为一个n个顶点的无向图,每个顶点中为两个人,两个顶点存在边时当且仅当两个顶点中能构成一对情侣,若是第一种情况,则构成重边。 相连的顶点构成圈,圈里面的若为第二种情况,所以对每一个圈来说有n个顶点就至少需要n-1次交换,若为第一种情况,即有重边,只需一次交换,也是n-1(两个顶点,2-1)。 因此若row有n组,m个圈,则至少需要n-m次。这和以上的结论是一样的,因为不同圈之间并不会发生交换。 其它解法 使用哈希配对的方法,两个数均除以2,若相等,则为同一组,若否,则区分出较大和较小的数,若两个数存在联系,则返回,若不存在,则建立这两个数的联系。最后根据哈希表中联系的个数来得到最小交换次数,原理同上面的一样,这里的联系就是指的是顶点之间的边,每个边需要一次交换,重边算作一次,所以每次要判断两个数是否存在联系。 代码: 12345678910111213141516class Solution {public: int minSwapsCouples(vector<int>& row) { unordered_map<int, int> m; for (int i = 0; i < row.size(); i += 2) { helper(m, row[i] / 2, row[i + 1] / 2); } return m.size(); } void helper(unordered_map<int, int>& m, int x, int y) { int c1 = min(x, y), c2 = max(x, y); if (c1 == c2) return; if (m.count(c1)) helper(m, m[c1], c2); else m[c1] = c2; }};","tags":[{"name":"Algorithms","slug":"Algorithms","permalink":"https://zhangziquan.github.io/tags/Algorithms/"},{"name":"LeetCode","slug":"LeetCode","permalink":"https://zhangziquan.github.io/tags/LeetCode/"}]},{"title":"数值计算实验一","date":"2018-09-12T11:46:27.000Z","path":"2018/09/12/数值计算实验一/","text":"数值计算实验 实验一 1.问题描述 求解线性方程组 Ax=b,其中 A 为 nⅹn 维的已知矩阵,b 为 n 维的已知向量,x 为 n 维的未知向量。 A 与 b 中的元素服从独立同分布的正态分布。令 n=10、50、100、200,测试计算时间并绘制曲线。 (1)高斯消去法。 (2)列主元消去法。 2.算法设计 高斯消去法 (1)消元:使用逐次消去未知数的方法把原线性方程组Ax = b化为与其等价的三角形线性方程组。 在这里使用将方程乘以某一乘数加到其它方程上以进行消元,乘数为两个未知数之商。得到等价的三角形线性方程组。 (2)回代:求解上三角线性方程组利用回代的方法,从下至上求出方程组中的一方程的未知数,然后逐层回代求出其它方程的未知数。 列主元消去法 与高斯消去法类似,在消元阶段进行交换行,选取绝对值最大的元素作为主元素,避免主元素为0,无法进行消元,以及当主元素很小时,作除数导致数值不稳定产生较大的误差。 回代方法和高斯消去法一致。 3.数值实验 随机构造出多个矩阵以及多个向量,矩阵和向量的维数n分别为10、50、100、200,同时使用高斯消去法,列主元消去法进行线性方程组的求解,比较两种算法的计算时间以及其计算时间与矩阵的维数(大小)的关系。 实验结果一: test1 实验结果二: test1-1 进行拟合: 拟合 4.结果分析 通过进行计算时间的测试,并绘制曲线得到实验结果,观察可得列主元消去法因为要进行查找绝对值最大主元素,并进行交换使得计算时间比普通高斯消去方法稍长,但两者的计算时间还是相近。 另外当矩阵的维数(大小)变大,这种消元回代方法的计算时间也大幅度增长,通过函数的拟合发现其计算时间为O(n^3)的,n为矩阵的维数。 实验二 1.问题描述 求解线性方程组 Ax=b,其中 A 为 nⅹn 维的已知矩阵,b 为 n 维的已知向量,x 为 n 维的未知向量。 A 为对称正定矩阵,其特征值服从独立同分布的[0,1]间的均匀分布;b 中的元素服从独立同分布的正态分布。 令 n=10、50、100、200,分别绘制出算法的收敛曲线,横坐标为迭代步数,纵坐标为相对误差。比较Jacobi 迭代法、Gauss-Seidel 迭代法、逐次超松弛迭代法、共轭梯度法与高斯消去法、列主元消去法的计算时间。改变逐次超松弛迭代法的松弛因子,分析其对收敛速度的影响。 2.算法设计 Jacobi迭代法 雅可比迭代法的思想在于把当前的x初始量当成方程组的解,从而进行回代得出下一步x的近似解,这种方法每次迭代都使用上一次迭代的x,若迭代矩阵谱半径<1,则在一定迭代次数后,x会收敛成精确解。 Gauss-Seidel 迭代法 高斯-赛德尔迭代与雅可比迭代类似,但是在每次迭代之中使用变量的最新信息计算x(k+1),是雅可比迭代法的改进。 逐次超松弛迭代法 逐次超松弛迭代是高斯-赛德尔迭代的一种修正,当w 为1 时即为高斯-赛德尔迭代,即在每一步的迭代后,对上一步的\\(x^(k)\\)和迭代后的x‘进行加权运算 x(k+1)=(1−w)∗x(k)+w∗x(k)x(k+1) = (1-w)*x(k)+ w*x(k)x(k+1)=(1−w)∗x(k)+w∗x(k),超松弛迭代w>1,一般为1<w<2,相当于做外插,加快收敛速度。 共轭梯度法 将解线性方程组转化成求解一个等价的二次函数\\(f(x) = 1/2* x^T * A *x - b^T *x\\)极小化的问题,从任意起始点出发沿A的共轭方向进行线性搜索得到二次函数的极小点。 3.数值实验 四种迭代法的收敛 n = 10的收敛曲线:(SOR收敛因子w 为1.1) test2-1 n = 50的收敛曲线: test2-2 n = 100的收敛曲线: test2-3 n = 200的收敛曲线: test2-4 六种算法计算时间的比较 n = 10: test2-5 n = 50: test2-6 n = 100: test2-7 n = 200: test2-8 逐次超松弛迭代松弛因子(w)的影响 完整的图:(此时n =200) test3 Y:(0,0.5) 4.结果分析 根据实验二的结果可得在四种迭代法中,雅可比迭代法收敛性较差,据其改进的高斯-赛德尔则较好,而增加了加权平均进行外插运算的逐次超松弛迭代收敛速度更快,收敛速度最快的则是共轭梯度法。 而六种算法的计算时间中可以看出,当矩阵较小时,迭代法和消元法计算时间相差不大,矩阵较大时,共轭梯度法在计算时间中有着较大的优势,达到所需精度的迭代次数比其它迭代次数要少很多,并且每次迭代耗时较少。因为构造的矩阵并不是稀疏矩阵,所以在这种情况下,普通的消元法比其它迭代法所耗时间要少。 在分析松弛因子对收敛速度影响中,松弛因子对算法的收敛速度影响很大,选择合适的松弛因子能够减少算法的迭代次数,在较少的迭代次数得到较为精确的结果,对于不同的矩阵有着不同的最佳松弛因子,对于本次模拟的矩阵w较大则最好,但是w超过2之后,算法则不再收敛。 实验三 1.问题描述 在 Epinions 社交数据集中,每个网络节点可以选择信任其它节点。借鉴 Pagerank 的思想编写程序,对网络节点的受信任程度进行评分。在实验报告中,请给出伪代码。 2.算法设计 和pagerank相类似,预先给每个网络节点一个信任值Trust值为1/N,N为节点的数量,这样就能得到一个向量v代表每个节点的信任评分rank v=[1/n,1/n,1/n,……]v = [1/n,1/n,1/n,……] v=[1/n,1/n,1/n,……] 假定每个节点的对其他节点的总的信任程度为1,而且对每个其信任的节点都给予相同的信任程度,这样就可以得到一个信任转移矩阵A。 A(i,j)= 节点i对节点j的信任程度 每个节点的信任评分rank由信任它的节点决定,如果信任评分的节点本身的信任评分高,则给予的评分权重也高,反之给予评分权重较低。 所以通过计算 v‘ = Mv 即 v(i) = ∑其它节点信任评分v(j)*信任程度A(i,j) 可以得到新的一个信任评分,经过有限次的迭代,v’将会收敛稳定下来,得到的即是各个节点的信任评分。 改进 对于一些只信任自己的,对其它节点并未作出信任的网页节点,采用这种算法有2个缺点 1.使得信任评分都集中到那一循环的节点上。 2.对于新建立的网络节点来说,并不公平,因为一开始没有其他节点信任它,可能导致信任评分恶性循环。 因此添加一个权重占比α,原先的评分仅占α,然后默认每一个节点都对其它节点有一定程度(较小但不为0)的信任,这个信任程度影响评分所占的权重为1-α。因此迭代公式变为: v(i)=∑v(j)∗A(i,j)∗α+(1−α)/Nv(i) = ∑v(j)*A(i,j)*α+(1-α)/N v(i)=∑v(j)∗A(i,j)∗α+(1−α)/N α一般设定为0.85。 3.伪代码 A = [N,N]; α = 0.85;%设定权重 %构建转移矩阵,即信任关系矩阵 for i = 1: N for j = 1 : N A[i,j] = 1/n(n 为i的所有出链,即给予信任的节点数总数) end end %初始化信任评分rank v0(1:N) = 1/N; v = v0 %迭代 while norm(v-v0,'inf')>0.01 %当v逐渐收敛后结束迭代 v0 = v; for i = 1: N sum = 0; for j = 1 : N %计算其它节点给予该节点的信任评分 sum = sum + A(j,i)*v0(j)*α + (1-α)/N end v(i) = sum; end end disp(v');","tags":[]},{"title":"[LeetCode] 75. Sort Colors","date":"2018-09-09T02:46:00.000Z","path":"2018/09/09/Leetcode-75-Sort-Colors/","text":"Sort Colors week1 题目链接 题目描述 Given an array with n objects colored red, white or blue, sort them in-place so that objects of the same color are adjacent, with the colors in the order red, white and blue. Here, we will use the integers 0, 1, and 2 to represent the color red, white, and blue respectively. Note: You are not suppose to use the library’s sort function for this problem. Example: Input: [2,0,2,1,1,0] Output: [0,0,1,1,2,2] Follow up: A rather straight forward solution is a two-pass algorithm using counting sort.First, iterate the array counting number of 0's, 1's, and 2's, then overwrite array with total number of 0's, then 1's and followed by 2's. Could you come up with a one-pass algorithm using only constant space? 解题思路 这道题主要是对已知数的排序,数组中的每一个数的范围都已确定,因此用以往的排序如快排,归并排序并不合适,要做到一次遍历完成并使用常空间,所以我选择使用插排来完成。 先使用普通的插排发现时间会很慢,而在这个过程中发现一般的插排都是逐个比较的,而这道题目的特殊点在于数的种类较少只有3个,而且是已知的,那么我们可以记录下每个数应插到的位置,就节省了比较的过程。 记录下0,1,2的可插入点的前一位,比如插入1后,1可插点向后移,而2因为只能在1后,所以也向后移,而0不受1影响,插入点不变。最重要的一点是当插入0/1时,会替换掉原本的数,因为后面的数向后移动,可以当作(1,2)/2也插入到数组中(后移替换掉原本的数)。 代码 123456789101112131415161718192021222324252627282930class Solution {public: void sortColors(vector<int>& nums) { int red = -1, white = -1, blue = -1; for(int i = 0; i < nums.size(); i ++) { if(nums[i] == 0) { red++; white++; blue++; nums[blue] = 2; nums[white] = 1; nums[red] = 0; } else if(nums[i] == 1) { white++; blue++; nums[blue] = 2; nums[white] = 1; } else { blue++; nums[blue] = 2; } } }}; Note 值得注意的是我们在写的时候,要把要插入的步骤写在其它数做完后移操作之后,因为存在一种可能假设现在整个数组中还不存在1,当你要插入0时,此时0和1的插入点是相同的,假设你先插入再后移,会使得插入的0被1替换掉(因为我们的后移操作是假设1的插入点前是1,0的插入点前是0),反之1被0替换则正确。 这种算法适合于已知数且不同数较少的排序,用替换取代后移,节省时间和空间。 时间复杂度和空间复杂度均为常数。","tags":[{"name":"Algorithms","slug":"Algorithms","permalink":"https://zhangziquan.github.io/tags/Algorithms/"},{"name":"LeetCode","slug":"LeetCode","permalink":"https://zhangziquan.github.io/tags/LeetCode/"}]},{"title":"I am a title","date":"2018-09-05T07:59:00.000Z","path":"2018/09/05/I-am-a-title-1/","text":"Welcome to StackEdit! Hi! I’m your first Markdown file in StackEdit. If you want to learn about StackEdit, you can read me. If you want to play with Markdown, you can edit me. Once you have finished with me, you can create new files by opening the file explorer on the left corner of the navigation bar. Files StackEdit stores your files in your browser, which means all your files are automatically saved locally and are accessible offline! Create files and folders The file explorer is accessible using the button in left corner of the navigation bar. You can create a new file by clicking the New file button in the file explorer. You can also create folders by clicking the New folder button. Switch to another file All your files are listed in the file explorer. You can switch from one to another by clicking a file in the list. Rename a file You can rename the current file by clicking the file name in the navigation bar or by clicking the Rename button in the file explorer. Delete a file You can delete the current file by clicking the Remove button in the file explorer. The file will be moved into the Trash folder and automatically deleted after 7 days of inactivity. Export a file You can export the current file by clicking Export to disk in the menu. You can choose to export the file as plain Markdown, as HTML using a Handlebars template or as a PDF. Synchronization Synchronization is one of the biggest features of StackEdit. It enables you to synchronize any file in your workspace with other files stored in your Google Drive, your Dropbox and your GitHub accounts. This allows you to keep writing on other devices, collaborate with people you share the file with, integrate easily into your workflow… The synchronization mechanism takes place every minute in the background, downloading, merging, and uploading file modifications. There are two types of synchronization and they can complement each other: The workspace synchronization will sync all your files, folders and settings automatically. This will allow you to fetch your workspace on any other device. To start syncing your workspace, just sign in with Google in the menu. The file synchronization will keep one file of the workspace synced with one or multiple files in Google Drive, Dropbox or GitHub. Before starting to sync files, you must link an account in the Synchronize sub-menu. Open a file You can open a file from Google Drive, Dropbox or GitHub by opening the Synchronize sub-menu and clicking Open from. Once opened in the workspace, any modification in the file will be automatically synced. Save a file You can save any file of the workspace to Google Drive, Dropbox or GitHub by opening the Synchronize sub-menu and clicking Save on. Even if a file in the workspace is already synced, you can save it to another location. StackEdit can sync one file with multiple locations and accounts. Synchronize a file Once your file is linked to a synchronized location, StackEdit will periodically synchronize it by downloading/uploading any modification. A merge will be performed if necessary and conflicts will be resolved. If you just have modified your file and you want to force syncing, click the Synchronize now button in the navigation bar. Note: The Synchronize now button is disabled if you have no file to synchronize. Manage file synchronization Since one file can be synced with multiple locations, you can list and manage synchronized locations by clicking File synchronization in the Synchronize sub-menu. This allows you to list and remove synchronized locations that are linked to your file. Publication Publishing in StackEdit makes it simple for you to publish online your files. Once you’re happy with a file, you can publish it to different hosting platforms like Blogger, Dropbox, Gist, GitHub, Google Drive, WordPress and Zendesk. With Handlebars templates, you have full control over what you export. Before starting to publish, you must link an account in the Publish sub-menu. Publish a File You can publish your file by opening the Publish sub-menu and by clicking Publish to. For some locations, you can choose between the following formats: Markdown: publish the Markdown text on a website that can interpret it (GitHub for instance), HTML: publish the file converted to HTML via a Handlebars template (on a blog for example). Update a publication After publishing, StackEdit keeps your file linked to that publication which makes it easy for you to re-publish it. Once you have modified your file and you want to update your publication, click on the Publish now button in the navigation bar. Note: The Publish now button is disabled if your file has not been published yet. Manage file publication Since one file can be published to multiple locations, you can list and manage publish locations by clicking File publication in the Publish sub-menu. This allows you to list and remove publication locations that are linked to your file. Markdown extensions StackEdit extends the standard Markdown syntax by adding extra Markdown extensions, providing you with some nice features. ProTip: You can disable any Markdown extension in the File properties dialog. SmartyPants SmartyPants converts ASCII punctuation characters into “smart” typographic punctuation HTML entities. For example: ASCII HTML Single backticks 'Isn't this fun?' ‘Isn’t this fun?’ Quotes "Isn't this fun?" “Isn’t this fun?” Dashes -- is en-dash, --- is em-dash – is en-dash, — is em-dash KaTeX You can render LaTeX mathematical expressions using KaTeX: The Gamma function satisfying Γ(n)=(n−1)!∀n∈N\\Gamma(n) = (n-1)!\\quad\\forall n\\in\\mathbb NΓ(n)=(n−1)!∀n∈N is via the Euler integral Γ(z)=∫0∞tz−1e−tdt.\\Gamma(z) = \\int_0^\\infty t^{z-1}e^{-t}dt\\,. Γ(z)=∫0∞tz−1e−tdt. You can find more information about LaTeX mathematical expressions here. UML diagrams You can render UML diagrams using Mermaid. For example, this will produce a sequence diagram: 123456789sequenceDiagramAlice ->> Bob: Hello Bob, how are you?Bob-->>John: How about you John?Bob--x Alice: I am good thanks!Bob-x John: I am good thanks!Note right of John: Bob thinks a long<br/>long time, so long<br/>that the text does<br/>not fit on a row.Bob-->Alice: Checking with John...Alice->John: Yes... John, how are you? And this will produce a flow chart: 12345graph LRA[Square Rect] -- Link text --> B((Circle))A --> C(Round Rect)B --> D{Rhombus}C --> D","tags":[{"name":"test","slug":"test","permalink":"https://zhangziquan.github.io/tags/test/"}]}]