-
Notifications
You must be signed in to change notification settings - Fork 0
/
content.json
1 lines (1 loc) · 167 KB
/
content.json
1
[{"title":"数据岗位的面试经历","date":"2018-01-05T13:46:02.000Z","path":"2018/01/05/view1/","text":"前言算起来应该是上周四开始投简历的,到现在一共收到两个面试通知,其中有一个还是非计算机专业的,面试的是股票交易员,此处有笑哭脸。另外一家就是今天要讲述的:我面试的这个职位。我先卖个关子,讲讲我之前面试的经验,以及我现在的决定。 关于之前面试的经验2016年6月那会,我只身来到北京,多亏遇到之前的学姐,帮我搞定了住宿的问题,之后我就静下心来开始投递简历了。那会我第一个想投递的是C语言开发工程师,投递了一番后并没有什么动静,一个面试通知也没有收到;后来改变了策略投递了PHP开发工程师,投递了一番后也是没什么动静;最后就数据库、C#等一起投递,最后在数据库和C#中各收获到了两个offer。这之中一共用了两周的时间,那两个offer都是在第二周周末的时候收到的,面试了十多家。从这之中我收获了一些面试的经验,第一是不要碰见谁都瞎吹,第二就是期望工资不能掉。其实最重要的并不是这项技能,最重要的技能是我知道如何与他人交流,知道了别人想听我说什么话。这一点我只能保证是技术上的话,要不有这项技能就太牛了。 关于这次面试我照常用着平常的方法去写简历,投递了算法工程师职位。一周后没有一个面试通知,这一周以及这半年,我都在做关于算法方面的准备。关于得到这样的情况我考虑到了其中一个是客观条件:年末的确不容易找工作。其二是,先讲技术上的,算法不仅要求你有基础的数据结构和算法的知识而且还要有机器学习或者NLP或者推荐系统等等关于现在最火的AI相关的技术;之后来聊一聊出生,一些大厂默认是不要这种普通二本院校的本科毕业生的。也就是说那些AI相关的我不仅不会而且还没什么经验。接着来聊今天面试的职位,我想这个不行,我就接着搞之前有过一些经验的数据库相关的事情,其实数据库的也投了不少,薪金从低到高不等,但接到面试通知的就只有这一家,关于为什么只有一家我想是因为上面说的客观原因了。接着就进入正题,来聊这次面试经历。我还是就这以后机会多呢的心态,去之前我什么也没准备,其实也想过会有笔试,但是没想到竟然会有机试。当hr小姐姐对我说把这几道题做了的时候,我还是有些慌的,hr小姐姐说你看着这些软件,想用什么就用什么,最后给个结果就可以了。我第一反应是用sql,当我去搞csv导入的时候发现导入不进去,而且那个sql客户端还是之前没用过的,但是见过。现在想来,sql还是能最快上手,只是不太记得charindex()和substring()的用法了。之后我用C后发现其实有几个问题还是蛮有难度的,所以sql中的这两个函数可能是不能解决的。后来我打开了vc,然而vc的编译器和gcc是略有不同的,刚开始一直因为定义变量而报错,搞得我很郁闷。所以一度放弃了实现,而是去思考问题的解决思路。后来我又打开eclipse发现连一个main都写不出来,那一刻真是有些崩溃了。直到那时我才明白我去面试的是数据相关的职位,而不是算法相关的职位。最后我还是硬着头皮用C把每个题目写了出来,思考问题的时候我是安静的,不知不觉中hr小姐姐过来问题写的怎么样的,我那时才写到第4个题,因为要出结果,我不知道c中的读写文件函数怎么写,那时我就直接告诉hr小姐姐能不能直接给技术面试官讲思路而没有结果。hr小姐姐说这样你的技术环节分数会很低的,之后我就用手机百度了c读取文件函数怎么写。又埋头写了起来,不知不觉两个小时就过去了,hr小姐姐也等不急了说,进入下一个环节吧,我只能报以歉意的微笑给她了。接着就有个技术面试官来看我做的题目了,后来知道,出题的面试官出差了,来看我题的是顶职的那位。他跟我聊了正则的事情,其实在答题的时候我就考虑的正则,因为真是手不熟,正则也写不出来,其实来的路上就看了正则相关的东西,但也只是看看,心存侥幸。面试官也不建议我用c写,我想也是这样的,那题目明显是sql中的每个记录,用sql或者正则是很容易处理的,关于后面文本的用正则是最好处理的。接着就是三位面试官约到咖啡店里继续聊了聊,那位之前给我看题的那位也在。他们主要问了我之前那个医疗处理项目相关的事情,这点对亏我之前又温故了之前所做的项目,现在看来,之前所做的事情我能更加明白了,我说的是在非技术方面的事情,所以当面试官1问到你之前做的是什么的时候,我能清楚的表达出来。后来关于自己主动说的地方,说出自己认为自己学习能力强这点的原因是有些牵强,还不如不说。我记得我说学到的东西全应付考试了,感觉跟自己学习能力强并没有什么关系,所以这并不是一个解释自己学习能力强的一个好的原因。还有一点就是我主动向面试官说自己对算法感兴趣,这一点我觉得也是败笔,有点炫耀技术的意味,当然也被其中一位面试官抓住了,本来是想问一些算法类的东西的,后来被另一个面试官制止了,说人家水平并没有那么高,我又跟着解释了只会一些基础数据结构和算法,接着被问到了快速排序,这正是我没有准备到的,算法和数据结构才写到树就搁置在那了。最后也没有解释清楚快排的原理,我想这位面试官对我的评价应该是及低了。最后就是又跟hr聊了,这倒是第一次见,因为之前面试的时候,hr会在之前问你一些基础简单的问题,之后是技术面,最后是部门经理面。一共来了两个hr小姐姐,问了一些常识的东西,这样就比较放松了,当问到我的职业规划的时候,我聊了我本来是想应聘算法工程师的,之后就聊之后自己想走算法方面的事情。这时比较专业的hr小姐姐来了,他问了我之前项目经验一类的事情,问的都是比较专业的事情,但我知道她也不太懂,不过我也没有戳穿。有几个问题我回答的不太好,主要是关于我做题的问题。我不应该为做不出来题去找理由,比如sql客户端的问题,此时hr小姐姐说难道是我们软件装的太少了吗?多亏我没有正面回答这个问题,因为直觉不能那样说,那一刻我也意识到我的表达已经让hr小姐姐觉得我在给自己做不出来题而找理由。我当时聊了sql相关的情况,也多亏了hr小姐姐不懂技术,其实我那么聊也是在谴责没有workbench。不过hr小姐姐说开发都是mysql了,我说对,也算是躲过了这一劫。最后我感觉自己回答比较出彩的是在hr小姐姐提问还有什么问题要问的时候,这时候我总算用到了剑指offer中关于这个问题的回答策略,别问那么有的没的,比如工资的事情,而要去问问公司最近在做什么项目了。我当时就这样回答了,问了公司相关的事情,开题是问我们的产品主要为哪些领域服务的,hr小姐姐说现在正在为医疗行业服务,后来详细问了问,当hr小姐姐说到健康医疗的时候我意识到hr小姐姐可能对这些也并不是很懂的时候,就不在去深究了。最后hr小姐姐说要面试我的技术人员出差了,说可能要等到下周再过来一次。 总结关于这次面试自己的不足是明显的,手都不熟,可以说是没准备,裸面。其他收获也是蛮多的,比如面试官对自己的评价,可塑性强。还有关于现在工作不好找,因为年末了。还有普通本科面试算法的。其实现在想来后两个问题并没有什么实际意义,因为,并没有其实质意义,虽然情况是这样了,但我这一月的租金已经出了,所以我还要接着找下去。做好每次面试的准备! 正则最后用来总结正则,这两天需要抓紧时间,把之前遗留的事情做完了。关于正则表达式,我想非计算机专业的同学可能也使用过,如果你用过word文档的查找功能,那便是正则表达式。正则起源于两个神经学家,他们研究出一种模型,认为神经层面就是这样工作的。正则也分了很多流派,最流行的当属Perl,Perl之所以闻名是它那会踏上了互联网的大潮。我初次接触正则的时候是在linux上,那会使用了一个叫grep的命令,现在知道这个grep的全称是global regular expression print,通常grep要同管道命令一起使用,也就是“|”这个符号。当初使用这个命令也就是为了查找方便,不然你的眼睛要去199个文件中找一个名字叫name的文件,是多么费劲的一件事情。后来在c中看到了很多关于字符串操作的相关算法题目,比如匹配问题,以及返回多次匹配,那会没有想到正则,因为在我感觉正则就是查查文件名称的用途了。再后来就是在工作中,又一次意识到了正则可以便捷的解决文本相关的问题。那时就快速的学习了一番,直接上手来用了,才发现这并不是一个简单的事情了。接着我用Python来写正则。先找一波基础知识贴过来,之后再测试一些熟悉正则的例子,最后我回忆面试题,试着写一下。基础知识最近这两天都在看正则,刚开始的时候去网站浏览的快速的教程,发现很多问题,看教程想不明白,后来又找来了《精通正则表达式》这本书,果然还是书上讲的清楚明白。我以学习语言的经验来聊正则表达式。在精通正则这本书中,把正则说成了是一门编程语言,当然我现在不知道正则的思维是如何的。就像是每种编程语言都会讲的一些东西,标识符、逻辑判断、分支循环结构一样,正则也存在着这样的语法,我不知道改如何去描述这一点,也就是说正则有些基本的东西你应该彻底掌握。元字符:也就是通常字符,每个字符,也可以是特殊字符,比如 . 等,代表非回车符的任意字符。字符集:是有多个字符,通常是指[]里面的,这里有个概念要和后面的一个概念区别一下。这个字符集的意思是[dsfd]中括号里面可以有任意字符。但匹配的时候是或的关系,其实,字符集[a-zA-Z0-9_]和元字符 . 的功能是一样的。子表达式:这是一个比前面两个概念有点高级,但本身又是很基础的知识,但大多复杂的问题,都可以通过子表达式来解决,表示的形式就是()。其实子表达式(a|b)和字符集[ab]的功能是一样的。这也就是上面提到的,字符集和子表达式看似详细的地方。但最大的区别是子表达式(hello|world)可以是一个模式,你这里可以理解为可以是多个字符的,而字符集[jfdsljfd]这里面只能匹配一个字符。还有另一个区别是字符集具有的[^a]匹配不是a的任意字符,而子表达式不具有取非得操作。量词:量词也挺多的,我偏向于把量词类比为编程语言中的循环结构。量词可以修饰元字符、字符集或者子表达式,这里我用了修饰这个词,但修饰的方法和咱们语言类的修饰是不同的,我们自然语言通常把量词放在放在将要修饰名词的后面,而这里的量词通常把修饰元字符、字符集或子表达式的后面。而且这个量词是可选的,强大吧,也就是以或的关系去修饰的,比如a。如果文本是aaaa,返回成功,如果文本是bbbb,照样返回匹配成功。也就是a这个表达式的意思是在指定文本中找字符a,a可以是有也就是1个a或者2个a或者多个a,a也可以没有,也就是0个a。其实a*和a{0,}表达的意思是一样的。我觉得正则最基本的语言也就是上面说的这几种,说出来后发现好像没彼此没什么太大的联系,也想不出设计者,为什么这么去设计。接着来聊Python,看看Python是如何处理正则的。下面我多介绍如何使用了。首先import re,正则的库。接着re.compile()。你可以把正则表达式,也就是上面说的那些基本概念写在这个函数的括号中,类似下面这样。1pattern = re.compile(r'我喜欢你') 可以说任何一门语言都会支持正则了,所以说正则的流派也特别多,这就像其他任何事物一样,比如c,gcc编译和vc编译要求都不大相同。精通正则上使用的是egrep,如果要写这个命令的话,是这样的。1egrep '我喜欢你' filename 之后你就可以使用pattern.match(),pattern.search(),pattern.findall()。其中要说明一下march和search的区别,看下面的例子。1234567891011s1 = '你知道吗,我喜欢你'#s2 = '我喜欢你'p1 = re.compile(r'我喜欢')m1 = p1.match(s1) #可匹配s2p2 = re.compile(r'我喜欢')m2 = p2.search(s1)print m1print m2.group()#输出结果None我喜欢 也就是说match是匹配开头,跟正则中的anchor有点相同,也就是必须第一个字符是我,第二个字符是喜,第三个字符是欢才能匹配成功,而search就是不一定是开头是我,才返回成功。findall是要找到所有匹配的子表达式,并且以列表的形式返回。最后是自己总结的一张表,其实就是看看网络上的教程,写了几个自己觉得常用的。实际操练本来想是找个相关的教程,看看别人的需求写一个,后来发现一个网址,harker rank。之后就在这个网站上刷了几道正则的题。前几道都是基础型的,最后一道是一个综合类型的,是要匹配邮件地址。通过实操对正则和Python都有了练习。邮件匹配测试的地址。最后还发现一个正则测试的网站,用着很顺手,也贴过来给大家分享。最后我直接上代码了,下面的代码通过了所有的测试。要谈几点,findall()返回的是一个list,包含所有匹配的结果。我一直以为是一个match对象的组合,其实是一个list。还有一点,如果你正则中有分组的话,findall()也会以二维列表的形式返回,这一点可以理解成为match对象的组合,也就是一个match是多个列表,如果有分组的话。第二点是python的print函数输出不换行的问题,需要顶行写出,之后用的时候是下面的形式,也就是加一个end = ‘’。12from __future__ import print_functionprint('hello world', end = '') 第三点是要谈的输出重定向问题,也就是把代码中的stdin和stdout转向文件的操作,这里有必要记录一下,因为对于oj练习来说是蛮重要的,你不可能手动输出那么多字符。也就是在调用函数前加上下面两句即可,其中test.in文件必须存在,test.out文件可以不用创建,而且第二次写出test.out文件时,第一次的内容会被清楚,也就是你不用手动去删除test.out,来保持测试内容是新的。12sys.stdin = open('test.in', 'r')sys.stdout = open('test.out', 'w') 最后是代码,关于邮件匹配的。1234567891011121314151617181920212223242526272829303132333435from __future__ import print_function# coding:utf-8import sysimport reimport osdef input(): n = int(raw_input()) i = 1 l = [] while(i <= n): l.append(raw_input()) i = i + 1 return ldef detect(): r = [] l = input() p = re.compile(r'\\w+\\.?\\w+@\\w+(?:\\.\\w+)+') for s in l: m = p.findall(s) if m: for md in m: r.append(md + ';') r = list(set(r)) r.sort() s = r[len(r) - 1] r[len(r) - 1] = s[:len(s) - 1] for s in r: print(s, end = '')sys.stdin = open('test.in', 'r')sys.stdout = open('test.out', 'w')detect()","tags":[{"name":"SQL","slug":"SQL","permalink":"http://turingxi.top/tags/SQL/"},{"name":"正则","slug":"正则","permalink":"http://turingxi.top/tags/正则/"}]},{"title":"重建二叉树","date":"2017-12-27T05:04:35.000Z","path":"2017/12/27/v7/","text":"问题描述输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2, 4, 7, 3, 5, 6, 8}和中序遍历序列{4, 7, 2, 1, 5, 3, 8,6},则重建出图所示的二叉树并输出它的头结点。 分析刚看到问题的时候感觉比较棘手,之前写过一个根据前序和中序输出后序的第一个元素的题目,感觉跟这个是有些相似。后来认真分析问题后,觉得第一个解决的是如何创建一个二叉树,这在那篇数据结构和算法中写了一个是通过中序序列来创建的,那个创建要求是把一个树按照扩展的前序序列输入,一个节点没有左右子树的话就用特殊字符代替。比如这棵树的扩展的前序序列为:124#7###35##68###,之后通过一个递归算法来创建二叉树。之后我就在想用这种方法来重建二叉树,我只要通过前序和中序序列找到扩展的前序序列即可。接着就来分析如何通过中序和后序来构建扩展的前序序列。其实在分析这个问题的时候,你可以用大脑,通过前序和中序来把这个二叉树画出来,步骤是这样的,首先在前序中找到第一个元素,那么这个元素一定是这个树的根,之后去中序遍历中找到这个节点,那么在中序序列中,这个节点的左边就是左子树,这个节点的右边就是右子树。之后你需要在前序序列中找到这两个子树。最后就是重复上面的过程就可以了,如果没有了子树就是叶子节点了。算法的思路也是根据上面的想法写出来的,上面的过程明显是一个递归的过程。而关键是找到根的位置,以及前序和中序中子树的范围。C语言实现123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687#include<stdio.h>#include<stdlib.h>#define MAX 65536int pre[MAX];int in[MAX];int expre[MAX];int plen, exlen = 0;int low = 0;typedef struct t{ int key; struct t *right; struct t *left;}T;void creat(T **t){ int k = expre[low++]; if(k == -1) *t = NULL; else { *t = (T*)malloc(sizeof(T)); (*t)->key = k; creat(&(*t)->left); creat(&(*t)->right); }}void preorder(T *root){ if(root != NULL) { printf(\"%d \", root->key); preorder(root->left); preorder(root->right); }}void input(){ scanf(\"%d\", &plen); int i = 1; while(i <= plen) { scanf(\"%d\", &pre[i]); i++; } i = 1; while(i <= plen) { scanf(\"%d\", &in[i]); i++; }}int seek(int low, int high, int root){ while(low <= high) { if(root == in[low]) return low; low++; } return -1;}void calc(int plow, int phigh, int inlow, int inhigh){ expre[exlen++] = pre[plow]; int index = seek(inlow, inhigh, pre[plow]); if(inlow < index) calc(plow + 1, plow + (index - inlow), inlow, index - 1); else expre[exlen++] = -1; if(inhigh > index) calc(plow + (index - inlow) + 1, plow + (index - inlow) * 2, index + 1, inhigh); else expre[exlen++] = -1;}int main(){ freopen(\"v7.txt\", \"r\", stdin); input(); calc(1, plen, 1, plen); T *t = NULL; creat(&t); preorder(t); puts(\"\"); return 0;} 这次没写测试用例,人眼把剑指offer的测试用例都测试了一下,都通过了。其实细节要考虑的是左子树和右子树的范围问题,这一点我写的比较粗糙。也就是代码83到91这之间的内容。","tags":[{"name":"Python","slug":"Python","permalink":"http://turingxi.top/tags/Python/"},{"name":"剑指offer","slug":"剑指offer","permalink":"http://turingxi.top/tags/剑指offer/"}]},{"title":"两个栈来实现一个队列","date":"2017-12-25T12:02:10.000Z","path":"2017/12/25/v9/","text":"问题描述用两个栈来实现一个队列分析这个题目其实是在考你对栈和队列的性质是否掌握的是否清晰明白。刚开始我大致想了几分钟没有想出来,后来去看了剑指offer上的指导,发现跟我想的是蛮接近。现在我再来捋一下思路,首先是实现队列,队列的性质是先进先出,而此时实现的条件是通过两个栈来实现。我们可以先考虑把元素存起来,也就是入队操作,因为你这个函数不可能什么也不做,最基础的一部肯定是要先存储起来。所以第一步操作就是将入队的元素存储在s1中,s1需要入栈来存储这些元素。如果此时需要出队呢?我们一定是要把s1中栈底的元素出队,而此时他却在栈底,通过将s1的出栈的元素在入进s2,那么s1的栈底元素也就变成了s2的栈顶元素,如果此时出队就是s2的出栈操作。这里有个限制操作就是,只要是入队就是s1的入栈操作,只有是出队就是s2的出栈操作。何时需要将s1的元素出栈到s2呢,也就是s2为空栈的时候,并且这个操作是伴随着出队操作时才会去判断的。也就是在入队中,只需对s1进行入栈操作即可。出队时,需要判断s2是否为空,如果为空,就将s1的内容出栈到s2。接着下来考虑一下入队和出队的溢出操作,入队的溢出是队满,队满的判断是s1满,s2不空。出队的操作是队不空,队列空的判断是s2空,s1也空。ps:感觉这次的分析应该是糟糕透了,说了太多废话。Python实现123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778# coding:utf-8class S: def __init__(self): self.m = 65536 self.top = -1 self.l = [] def isempty(self): if(self.top == -1): return True return False def isfull(self): if(self.top == self.m): return True return False def push(self, key): if(not self.isfull()): self.l.append(key) self.top += 1 def pop(self): if(not self.isempty()): tmp = self.top self.top -= 1 return self.l[tmp] else: return Noneclass Q: def __init__(self): self.s1 = S() self.s2 = S() def isempty(self): if(self.s1.isempty() and self.s2.isempty()): return True return False def isfull(self): if(self.s1.isfull() and (not self.s2.isempty())): return True return False def enqueue(self, key): if(not self.isfull()): self.s1.push(key) else: return None def dequeue(self): if(not self.isempty()): if(self.s2.isempty()): while(not self.s1.isempty()): self.s2.push(self.s1.pop()) return self.s2.pop() else: return Nonedef test(): q = Q() q.enqueue(1) q.enqueue(2) q.enqueue(3) q.enqueue(4) a = [1, 2, 3, 4] f = 1 for i in a: if(i != q.dequeue()): f = 0 break if(f == 1): print(\"passed\") else: print(\"failed\")test()","tags":[{"name":"Python","slug":"Python","permalink":"http://turingxi.top/tags/Python/"},{"name":"剑指offer","slug":"剑指offer","permalink":"http://turingxi.top/tags/剑指offer/"}]},{"title":"数据结构和算法","date":"2017-12-22T11:32:02.000Z","path":"2017/12/22/datastruct/","text":"前言为什么要写这一篇呢?原因是我不够熟练,大多时候,比如说要写一个栈,我想一想是能够写出来了,可是当我再去细致思考时,比如我用什么来实现呢?数组,链表。有比如我用数组去实现队列,是用循环队列呢?还是普通队列。我现在又想起这些问题的时候,我发现还是自己掌握的不够扎实。还有一点就是记忆,我刚去看了之前写的哈夫曼,虽然也用到了树结构,但是现在又去看,还是觉得在基础上模模糊糊的,虽然也敲了很多天代码才写成,但是什么也没有留下。所以这次决定从线性结构到图,把一般常用的数据结构以及算法再梳理一遍,所以这篇文章应该会比较长。 数据结构线性线性表线性表也就是大部分常在使用的数组,所谓线性也就是跟一根线一样,直直的,所有的数据够挂在这根线上。这里不再讨论数组的实现和创建,一般这个比较简单,大部分语言已经集成了这个功能。此处就直接来讨论链式存储,链式存储就跟冰糖葫芦一样,只不过形式上和冰糖葫芦还不太像,因为冰糖葫芦是一根棍子穿着所有的山楂,把链这个字没有体现出来。不知道大家有没有见过一种武器,武器是由一个铁球和一条铁链子,这个铁球表面上还有很多坠状的金属,这个形状跟仙人球很像,不过没有仙人球的刺那么密但比仙人球的刺的比例要粗很多。如果把这种武器和冰糖葫芦结合在一起,也就是把冰糖葫芦中的那根棍去掉,取而代之的是这种武器上的铁链子,然后把每个山楂串起来。这样的一个形状来解释链表的形式就清楚了一些。链表通常有三种操作,创建、删除节点、插入节点。我之前经常在创建上发生疑惑,疑惑点有两个,其一是带头与不带头,其二就是大部分喜欢用双重指针来构建链表。今天我就把这两个问题重新模拟一遍,把我通常创建方法与这两种疑惑点进行对比来看看。当我去思考上述两个问题的时候发现其实都是指针问题,而后我又把指针拿来揣摩了一番,其实大部分都跟我之前的认识是相同的,只不过长时间不去使用指针就把很多东西遗忘掉了,以至于不能熟练的去读懂别人的代码。接着我再次来详细的说明一下什么是指针,课堂中老师们经常用索引或者钥匙来说明指针,而在我眼里指针中其他int数据类型或者char数据类型一样也是一种数据类型只不过比较特殊。他特殊的一点在于他可以指向其实变量的地址,并且他通过一些特俗的操作可以去访问和修改他指向的那个变量的值。关于这点,如果有点汇编经验的同学可能一看就会明白,如果抛开汇编或者组成原理来讲这件事情的话就需要花费些心思了。如果把内存比作大海,指针就好比海里的鱼,还是那种比较凶猛的鱼,比如说鲨鱼。其实这个例子不太恰当,因为大海和内存不成正比,其二大海是三维的而内存是二维的。我接着说这个不太恰当的例子,首先鲨鱼也是鱼,就好比指针也是变量一样。这是事务的本质,这一点一定要看明白。其次,鲨鱼这种鱼可以游走是大海的任何地方,这一点就像指针一样,可以指向内存的从00000000-ffffffff任何一个地址。最后这个鲨鱼可以让所到之处变化样子,这就好比指针可以改变所指向变量的值一样。其实道理讲起来都比较简单,大家也都容易明白,但你依然过不好这一生,就像是你依然用不好指针一样。此处再来聊一聊双重指针或者多重指针,我感觉这个思想是有些递归的意味了,多重指针也就是指向多减1重指针的指针,比如说这里的多是i,那么也就是说,i重指针是指向i - 1重指针的指针,然而i - i重指针是指向i - 2重指针的指针,然后一路向前推,直到1重指针。这个意味有些螳螂捕蝉黄雀在后的感觉,只不过不是这3中动物,而是多种动物厮杀链。今天算是有看了一天关于指针的事情,自己也写了不少的测试代码。发现自己随便写的那些代码有很多UB的问题,UB也就是所谓的未定义的行为。关于指针其实还是要明白他的基本,指针只不过是一个变量,但是他存储的是其他变量的地址。接着讨论线性表的创建。线性表的创建一般分为两种方式,头插和尾插。我这里只来说明这两者的本质吧,所谓头插就是找到头结点,在头结点之后插入新的节点即可。所谓尾插就是找到尾节点,在尾节点之后进行插入。这两个插入操作的时间复杂度都是O(n),具体就看应用情况了,比如要实现链式存储的栈用头插就比较好,因为头插方法是按照先进后出的特点来输出元素的。下面的代码中我写了3中链表的创建方式,第一种是尾插的方法,也就跟我描述的一样找到尾节点,将其插入,如果链表是空的,就直接将带插入节点赋值给链表头指针即可。这里要谈一下头指针为何是双重指针,关于函数返回值,是有三种方式的,一种是return,一种的参数(地址传递),一种是全局变量,因为此处需要把p所指地址的内容传出去,故使用了双重指针,当然你也可以单指针,就好比我经常的写法,只不过要浪费掉一个节点。第二种是我经常使用的方法,是头插法,也就是输出内容的顺序与插入值得顺序是相反的。第三种也是头插法,此处你可以看到链表实现的方法是多种多样的,第三种是通过返回值来传出节点的地址的。写完这三种方法后,我得找一种我喜欢的,其实还是我经常写的那个比较顺手,但是浪费了一个节点。不过最主要的是这种思想,我之前出现的那些问题,现在也能想明白了,主要是关于指针和函数的返回值问题。现在看来都是基础的问题,实际中,你是要把基础的问题理解的深刻。123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596#include<stdio.h>#include<stdlib.h>typedef struct LinkList{ int key; struct LinkList *next;}L;void createoftail(L **head, int k){ L *p = (L*)malloc(sizeof(L)); p->next = NULL; p->key = k; if(*head == NULL) { *head = p; } else { L *t = *head; while(t->next != NULL) t = t->next; t->next = p; }}void me(L *head, int k){ L *p = (L*)malloc(sizeof(L)); p->key = k; p->next = head->next; head->next = p;}L *createofreturn(L *head, int k){ L *p = (L*)malloc(sizeof(L)); p->key = k; p->next = head; head = p; return head;}int test(L *head, int *a){ L *p = head; if(p == NULL) return 0; int f = 1; while(p != NULL) { if(*a != p->key) { f = 0; break; } a++; p = p->next; } if(f == 1) puts(\"passed\"); else puts(\"falied\");}void testoftail(){ L *h = NULL; L **head = &h; createoftail(head, 4); createoftail(head, 3); createoftail(head, 1); int a[] = {4, 3, 1}; test(h, a);}void testofme(){ L *head = (L*)malloc(sizeof(L)); head->next = NULL; me(head, 4); me(head, 3); me(head, 1); int a[] = {1, 3, 4}; test(head->next, a);}void testofreturn(){ L *head = NULL; head = createofreturn(head, 4); head = createofreturn(head, 3); head = createofreturn(head, 1); int a[] = {1, 3, 4}; test(head, a);}int main(){ testoftail(); testofme(); testofreturn(); return 0;} 关于剩下两种操作,其一是插入节点,实际上创建的过程就是插入节点的过程,其二是删除节点。删除节点通常有两种方式,一种是通过索引的方式,一种是删除指定值得方式。C有一点不好的地方就是不能像Java那样垃圾回收,也就是你删除节点后还需要你的手动把这部分的存储空间释放掉,在C中释放存储空间的函数是free。删除节点的思路比较简单,第一步是找到这个节点,之后将这个节点之前的那个节点的线连接在这个节点之后的那个节点上就可以了。下面是代码的实现,一种是通过索引,一种是通过指定的值。1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768int del(L **head, int key){ if(*head == NULL) return 0; L *one = *head; //链表中只有一个节点 if(one->next == NULL) if(one->key == key) { free(one); *head = NULL; return 1; } else //删除值不在链表中 return 0; //头结点是要删除的值 else { if(one->key == key) { *head = (*head)->next; free(one); return 1; } } L *two = one->next; while(two != NULL) { if(two->key == key) break; two = two->next; one = one->next; } //删除值不再链表中 if(two == NULL) return 0; //删除节点是尾节点 if(two->next == NULL) { free(two); one->next = NULL; return 1; } else { one->next = two->next; free(two); return 1; }}void testofdel(){ L *head = (L*)malloc(sizeof(L)); head->next = NULL; me(head, 4); me(head, 3); me(head, 1); L *h = head->next; del(&h, 3); del(&h, 4); del(&h, 1); del(&h, 1); if(h == NULL) puts(\"passed\"); else puts(\"failed\");} 刚开始写的那会还觉得比较简单,可是回想到剑指offer上说的,把问题想全面就发现问题开始繁琐起来,其实我是挺讨厌这种繁琐的问题,繁琐的问题和难的问题是不一样的,繁琐是将这件事的复杂也就是说需要你每一步都能想到,这样才能得出完美的解答。如果稍不留神想不到一个点,那么代码就会出现bug。在写上面的删除节点的问题时候,刚开始是想通过两个方式来删除节点,索引和值删除,本来是想写在一个函数里的,后来我想了更多的,比如删除的节点是头结点或者尾节点或者链表中只有一个节点的情况,对于这些处理的方式都是不同的,因此你要思考好这些问题的关系,也就是谁包括谁的问题。后来我就改成了只写一种,通过值来删除节点,而后我又想起一个问题,如果有多个值如何处理,我发现这就又繁琐了起来, 后来想到如果有多个值可以通过函数的返回值来写,也就是我在函数内部实现删除一个值,在函数外部通过返回值来判断多个值是否删除。这样保持的函数本身的简洁性,后来我在考虑关于如何设计多个函数以及期间的关系,也就是软件工程中所说的耦合和内聚问题了。所以说,简单的事情并不简单。 栈和队列栈和队列是特别简单的概念,估计任何你跟任何一个小学生聊这个问题,他们也是能明白什么是栈,什么是队列,这可能就是所谓的伟大的东西都是简单的东西。栈和队列在计算机领域是应用特别广泛和基础的两种数据结构,就拿栈来讲,函数的调用,其实这个具体实现我也不太明白,大家都拿这个来举例子,我也就再拿来这个来加深记忆。函数调用,比如在main()中调用,f1(),首先将main()的地址进栈,之后再将f1()的地址进栈。队列的应用我记得是在操作系统中比较常见,因为在进程调度中,很多进程等待着CPU的时候,会按照队列的形式在哪等着,就跟你去银行办理业务一样,得排队是一个道理。栈的性质是先进后出,队列的性质是先进先出。接下来我分别用数组的形式来实现栈和队列的基本操作,常用的基本操作就是初始化栈或者队列,入栈、入队、出栈、出队,判断栈或者队列是否为空。因为用数组了,所以下面的代码我用Python来写。1234567891011121314151617181920212223242526272829303132333435363738394041424344# coding:utf-8class s: def __init__(self): self.m = 65536 self.top = -1 self.l = [] def push(self, key): if(self.top != self.m): self.l.append(key) self.top += 1 def pop(self): if(self.top != -1): tmp = self.top self.top -= 1 return self.l[tmp] else: return Nonedef test(): k = s() a = [] t = [] a.append(4) a.append(3) a.append(2) a.append(1) for x in a: k.push(x) t.append(k.pop()) t.append(k.pop()) t.append(k.pop()) t.append(k.pop()) t.reverse() if(a == t): print(\"passed\") else: print(\"failed\")test() Python小记本来说是用Python去写的,打开vim就搞了C,后来又写了Python的,不得不说Python的数据结构封装的太现成了,去实现这些都不知道从何下手了,应该说是下手的地方太多了。第一次写Python的类,Python的面向对象和Java和C#的差别的确有点大。其中静态变量之间定义在类体中,也省了static这关键词。而后的私有变量是__开头就行,省了private。我感觉通常使用self关键字就可以定义变量了,用的时候也这样用就行了,self中定义的变量通过类名是访问不了的。也就是说在类里定义变量的话,如果没有什么特殊要求,也就是设计模式那一套,来搞算法的话,self.varname就可以了。 接着就来回忆一下队列。逻辑结构就不在细说了,队列的实现上,其实我觉得用链式是蛮简单的,一个尾指针和一个头指针,尾进,头出。如果队列为空的话就是尾指针和头指针相等。队列什么时候满这就要看内存空间大小了,当然你也可以人为的去设计一个计数器放在结构体中。之后来聊一聊数组实习的方式,数组通常是使用循环队列来实现的,这样的目的是为了提高空间利用率。队列为空的判断依旧是尾指针和头指针相等,队列为满的条件是尾指针 + 1 除以队列大小后去余数看是否等于头指针。这样的写法会使得队列损失一个元素,但不失为最好的方法。关于循环队列的实现关键其实是取余数运算,这样可以限制取得的余数一直在某个范围内,达到了循环的作用。算法导论里并没有使用取余操作,而是增加了一个判断,似的尾指针和头指针依旧在某个范围循环出现,操作是判断尾指针和头指针是否等于队列长度,如果等于就从新置为1。下面我用C来写队列的链式存储,用Python来写循环队列。 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879#include<stdio.h>#include<stdlib.h>typedef struct node{ int key; struct node *next;}node;typedef struct{ node *tail; node *head;}Q;void init(Q *q){ q->tail = NULL; q->head = NULL;}void enqueue(Q *q, int key){ node *p =(node*)malloc(sizeof(node)); p->key = key; p->next = q->tail; q->tail = p; if(q->head == NULL) q->head = p;}int dequeue(Q *q){ if(q->tail == NULL || q->head ==NULL) return 0; if(q->tail == q->head) { int key = q->tail->key; free(q->tail); q->tail = q->head = NULL; return key; } node *t = q->head; int key = t->key; node *h = q->tail; //找到倒数第二个节点 while(h != NULL) { if(h->next == t) break; h = h->next; } h->next = NULL; q->head = h; free(t); return key;}void test(){ Q *q = (Q*)malloc(sizeof(Q));; q->tail = NULL; q->head = NULL; enqueue(q, 1); enqueue(q, 2); enqueue(q, 3); enqueue(q, 4); int a[] = {1, 2, 3, 4}; int i = 0; while(i < 4) { if(dequeue(q) == a[i]) i++; else break; } if(i == 4) puts(\"passed\"); else puts(\"failed\");}int main(){ test(); 聊一聊用C写的链式存储,这次写的时候提前没有想明白,以至于去敲代码的时候发现了很多问题。其一链式的队列本质还是一个链表,不过是多了一个尾指针。之后就要考虑去实现了,关于参数的传出你可以用返回值或者函数参数的值传递。我觉得考虑这些跟如何设计结构体是有些关系的。我这里依旧使用了头插法,可能是习惯了,依旧用了头插的特性,插入后的值会是倒序,所以我就把插入的值用tail指针指着,这里从逻辑上讲有点相悖,也就是说我这种代码的可读性不会太高。因为在存储上讲它其实是头指针,而结合了队列去讲却成了尾指针。我之后说的都是结合队列来讲的,这里的头指针指向第一个入队的元素,出队操作写的就有些复杂了。其实我也考虑了尾插法因为尾巴插入在实现上是O(n),入队和出队,在逻辑上讲解的话会比较容易明白。我的头插在入队时是O(1),但在出队时是O(n)。通过这样的分析,我感觉应该把向链表插入元素和入队操作分开来写的话,会使得代码更加清晰。 123456789101112131415161718192021222324252627282930313233343536373839404142# coding:utf-8class Q: def __init__(self): self.m = 65536 self.l = [] self.tail = 0 self.head = 0 def enqueue(self, key): #损失一个存储空间,队列为满的情况 if(((self.tail + 1) % self.m) == self.head): return None self.l.append(key) self.tail = (self.tail + 1) % self.m def dequeue(self): if(self.tail == self.head): return None tmp = self.l[self.head] self.head = (self.head + 1) % self.m return tmpdef test(): q = Q() q.enqueue(1) q.enqueue(2) q.enqueue(3) q.enqueue(4) a = [1, 2, 3, 4] f = 1 for i in a: if(i != q.dequeue()): f = 0 break if(f == 1): print(\"passed\") else: print(\"failed\")test() 写的是循环队列,损失一个存储空间的,我觉得这种写法是挺高效的,如果用标记去做也可以,但是觉得会增加多次判断,还不如损失一个空间来的实在,这里处理循环队列的主要方法是通过取余操作。终于把线性结构总结完了,需要加快速度了,后面还有SQL和一些其他的琐碎东西到总结,不过线性结构是其他结构的基础。 非线性树树也是经典的数据结构,计算机里的树和现实生活中的树在结构上刚好相反,现实生活中,树的根是在土壤中,而计算机中的树的根是在天空上的,因此他们通常在解释树结构的时候会说成是一棵倒树。树可以推广到森林,但大部分情况下我们讨论的树是二叉树,当然森林也可以通过一些操作转为二叉树。二叉树就是说一棵树中每个节点只有0到2至亲个节点,通常把这些节点叫做父节点、左孩子、右孩子。关于二叉树的一些性质:1. 二叉树的第i层最多有2^(i - 1)个节点。2. 深度为k的二叉树最多有2^k - 1个节点。3. 叶子节点为n0个,有两个孩子的节点为n2个,那么n0 = n2 + 1。4. 具有n个节点的完全二叉树的深度为lgn + 1,结果向下取整。接着来说说树的应用,现在想起来的是哈夫曼,也就是常用的压缩算法。然而我觉得在树上厉害的其实是这个数据结构,比如下面介绍的红黑树或者B树,通常是在树的这个结构上做一些限定性条件,之后会觉得在树上算法就稍微逊色了一点,要不就是树上的算法太厉害了,我这个级别的还没碰到关于在树上做算法文章的。接着就来讨论一下树的实现,当然树也可以通过数组来实现,比如常用的堆就是通过数组来实现的,我当时也写过一个最小堆的树,不过我用的是兄弟孩子法。兄弟孩子法是链式存储,也就是一个节点有3个指针域,分别是左孩子、右孩子和父亲。其实关于树的创建上我是使用的比较少的,我记得之前写过一次树的创建,但是还得要求输入的次序好像还是前序什么的,记得不太清楚了,后来写了一个哈夫曼编码,当时用了最小堆,不过在最小堆结构和树的链式结构上没有抉择好,当时是浪费了好多存储空间。本来是想写压缩算法的,写到一半就放在那了。所以我认为树的创建应该有具体的应用场景,当然在这里我也得再重新温故一遍孩子兄弟法。 123456789101112131415161718192021222324252627282930313233343536373839#include<stdio.h>#include<stdlib.h>typedef struct t{ int key; struct t *right; struct t *left;}T;void creat(T **t){ int k; scanf(\"%c\", &k); if(k == '#') *t = NULL; else { *t = (T*)malloc(sizeof(T)); (*t)->key = k; creat(&(*t)->left); creat(&(*t)->right); }}void pre(T *root){ if(root != NULL) { printf(\"%c \", root->key); pre(root->left); pre(root->right); }}int main(){ T *t = NULL; creat(&t); pre(t); puts(\"\"); return 0;} 查阅了相关创建的二叉树的方法,有好几种,但我看着都没有什么意义。我写的这种是根据前序遍历来进行创建的,在输入的时候要将空指针输入成特殊的字符。比如一个二叉树的前序是abcd,那么它的形状是ab##cd###。也就是说在创建的时候要按照ab##cd###这样的输入。在C中又发现一个ub问题,%d是接受不了字符的,%d接受那些非数字外的其他字符都会输出1。堆红黑树B树图 算法方法论暴力回溯分治减治动态规划贪心 排序和查找归并快速哈希(散列)","tags":[{"name":"C","slug":"C","permalink":"http://turingxi.top/tags/C/"},{"name":"Python","slug":"Python","permalink":"http://turingxi.top/tags/Python/"}]},{"title":"替换空格","date":"2017-12-22T07:16:02.000Z","path":"2017/12/22/v5/","text":"问题描述请实现一个函数,把字符串中的每个空格替换成”%20”。例如输入“We are happy.”,则输出“We%20are%20happy.”。分析暴力找到空格就进行替换,按到常理的思路是按照从前往后的顺序题替换,这样每替换一次就要移动元素一次,故时间是O(n^2)从后往前刚读到题目的时候就想起来从后往前这个思路,因为可以减少移动元素的次数,通过一次遍历就可以完成元素的替换。之前写过一个两个有序表合成一个新的有序表的时候用的也是这样的思路。Python小记 Python对字符串进行扩容,我也不知道如何定义指定大小的字符串,Python中有一些函数可以直接扩容,比如str.center(size)或者是str.zfill(size),但这两个一个是居中填充,一个是左边填充0,可是这次需要右边填充,就写了一个函数。 123456 #左填充size个大小,填充的是空格符号 t = \" \"i = 1while(i <= size): string += t i += 1 Python不支持对字符串通过下标进行修改,比如在C中可以这样写 12char c[200] = \"hello world\"c[5] = 'N' 然而Python中不可以这样写,权限是可读不可写,故通过切片来解决这个问题123#string为待修改字符,index为需要修改下标,比如就像上面的20,char被修改的字符,像上面的'N'def replace(string, index, char): return string[:index] + char + string[(index + 1):] Python竟然没有++运算符,吐槽一下Python实现123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081# coding:-utf-8# py不支持对字符串按照下标直接修改值# 因此写了这个函数用来直接修改字符串# 中某个下标的值def replace(string, index, new): return string[:index] + new + string[(index + 1):] def replacespace(string): if(len(string) == 0): return False #统计原字符串长度 original = len(string) #统计空格出现次数 blank = string.count(\" \") #计算新字符串长度 new = original + blank * 2 #扩充字符串的大小 t = \" \" i = 1 while(i <= blank * 2): string += t i += 1 #Py的下标都是从0开始 original -= 1 new -= 1 while(original >= 0 and new > original): if(string[original] == \" \"): string = replace(string, new, '0') new -= 1 string = replace(string, new, '2') new -= 1 string = replace(string, new, '%') new -= 1 else: string = replace(string, new, string[original]) new -= 1 original -= 1 return stringdef test(s): expected = s.replace(\" \", \"%20\") result = replacespace(s) if(expected == result): print \"passed\" else: print \"failed\"def test1(): test(\"hello world\")def test2(): test(\" helloworld\")def test3(): test(\"helloworld \")def test4(): test(\"hello world\")def test5(): test(\"\")def test7(): test(\" \")def test8(): test(\"helloworld\")def test9(): test(\" \")test1()test2()test3()test4()test5()test7()test8()test9()","tags":[{"name":"Python","slug":"Python","permalink":"http://turingxi.top/tags/Python/"},{"name":"剑指offer","slug":"剑指offer","permalink":"http://turingxi.top/tags/剑指offer/"}]},{"title":"特定条件下的查找","date":"2017-12-20T13:15:11.000Z","path":"2017/12/20/v4/","text":"问题描述在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。分析思路1查找的暴力思路,O(n^2)思路2根据题目所给数组的特殊性质,行与列的值是依次递增的,根据这个性质,从右上角或者左下角的值开始于关键值key进行比较,如果同key相等就返回True,代表找到了,如果大于key就将row的值加1,如果小于key就将column的值减去1。我刚演算了一下,考虑了复杂度为O(n),我也不知道对不对,下面的算法是根据思路2来写的。思路3基于折半的思想,理论上可以达到O(lgn)。思路是取row和column的中间值,之后可以去掉3/n的元素,当然也要考虑矩阵是不是方阵,去掉之后会产生3个矩阵,之后再使用相同的方法去取那三个矩阵的中间值。依次类推,考虑了一下可以用递归写写试试,不过还是比较麻烦。Python小记 创建一个i行j列的数组,其实Python里不再叫数组了,而是列表。1mylist = [([0] * j) for k in range(i)] 吐槽一下C创建i行j列数组为1mylist[i][j] 取列表列数和行数12rows = len([x[0] for x in mylist])columns = len(mylist[0]) Python实现12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364# coding:utf-8# 特定条件下的查找def find(matrix, key): if(len(matrix) == 0): return False rows = len([x[0] for x in matrix]) columns = len(matrix[0]) if(rows > 0 and columns > 0): row = 0 column = columns - 1 while(row < rows and column >= 0): if(matrix[row][column] == key): return True elif(matrix[row][column] > key): column -= 1 else: row += 1 return Falsedef test(name, matrix, key, expected): if(name != \"\"): print name + \" begins:\" result = find(matrix, key) if(result == expected): print \"pass\" else: print \"failed\"def test1(): matrix = [[1, 2, 8, 9], [2, 4, 9, 12], [4, 7, 10, 13], [6, 8, 11, 15]] test(\"test1\", matrix, 7, True)def test2(): matrix = [[1, 2, 8, 9], [2, 4, 9, 12], [4, 7, 10, 13], [6, 8, 11, 15]] test(\"test2\", matrix, 5, False)def test3(): matrix = [[1, 2, 8, 9], [2, 4, 9, 12], [4, 7, 10, 13], [6, 8, 11, 15]] test(\"test3\", matrix, 1, True)def test4(): matrix = [[1, 2, 8, 9], [2, 4, 9, 12], [4, 7, 10, 13], [6, 8, 11, 15]] test(\"test4\", matrix, 15, True)def test5(): matrix = [[1, 2, 8, 9], [2, 4, 9, 12], [4, 7, 10, 13], [6, 8, 11, 15]] test(\"test5\", matrix, 0, False)def test6(): matrix = [[1, 2, 8, 9], [2, 4, 9, 12], [4, 7, 10, 13], [6, 8, 11, 15]] test(\"test6\", matrix, 16, False)def test7(): test(\"test7\", [], 7, False)test1()test2()test3()test4()test5()test6()test7()","tags":[{"name":"Python","slug":"Python","permalink":"http://turingxi.top/tags/Python/"},{"name":"剑指offer","slug":"剑指offer","permalink":"http://turingxi.top/tags/剑指offer/"}]},{"title":"数组中重复的数字","date":"2017-12-20T11:13:14.000Z","path":"2017/12/20/v3/","text":"问题描述在一个长度为n的数组里的所有数字都在0到n-1的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。例如,如果输入长度为7的数组{2, 3, 1, 0, 2, 5, 3},那么对应的输出是重复的数字2或者3。分析思路1根据元素的范围为0 - (n - 1)这个限制可以使用哈希的思想。初始化一个大小为n的数组A,数组全部赋值为-1,之后依次遍历目标数组B,判断A[B[i]]是否等于-1如果等于就将A[B[i]]赋值为i,如果不等于-1就说明找到相同的值了,并且返回A[B[i]]。这是一个时间和空间都是O(n)的算法。思路2这个思路是在哈希的基础上进行思考的,它把值的搜索和调整哈希放在了一起进行。具体实现依次遍历每个元素,之后判断是否和依次遍历的顺序值i相同,如果不相同就进行调整,使之有序(也就是哈希调整),是先进行判断,如果number[i] == number[number[i]],这一点其实和上面的A[B[i]]是有相似之处。如果不相等就去调整,如果相等就返回相等的值。Python实现12345678910111213141516171819202122232425262728293031323334353637383940414243444546# coding:utf-8# 查找数组中重复的数字# 时间O(n), 空间O(1)def duplicate(number): #列表为空 if(len(number) == 0): return False #列表范围在0 - (n - 1) if(not(min(number) >= 0 and max(number) <= (len(number) - 1))): return False #calc i = 0 while(i < len(number)): while(number[i] != i): if(number[i] == number[number[i]]): return number[i] tmp = number[i] number[i] = number[tmp] number[tmp] = tmp i += 1; #没有相同元素 return Falsedef test(): #一个重复的最小值:1 t1 = [2, 1, 3, 1, 4] print duplicate(t1) #一个重复的最大值:4 t1 = [2, 4, 3, 1, 4] print duplicate(t1) #两个重复的值:2 t1 = [2, 4, 2, 1, 4] print duplicate(t1) #没有重复的值:False t1 = [2, 1, 3, 0, 4] print duplicate(t1) #没有重复的值但超越取值范围:False t1 = [2, 1, 3, 4, 5] print duplicate(t1) #空:False t1 = [] print duplicate(t1)test()","tags":[{"name":"Python","slug":"Python","permalink":"http://turingxi.top/tags/Python/"},{"name":"剑指offer","slug":"剑指offer","permalink":"http://turingxi.top/tags/剑指offer/"}]},{"title":"读《数学之美》有感","date":"2017-12-18T08:10:23.000Z","path":"2017/12/18/读《数学之美》有感/","text":"前言早在大一的时候就听说过这本书,同一时间听说的还有一本叫《代码之美》。我是很少买书的,这算是第一本,当然除了买上课要用到的教材。当时买这本书籍的同时也买了另外一本,那本书是讲网络安全的。买这两本书的初衷为了减少自己无聊时候玩手机的时间。所以故意买了一些讲解比较通俗的又同我的专业有关的书籍。我是在今年夏天的时候买的这本书籍,今天把这本书看完了。首先我来介绍一下写这本书的作者,写这本书籍的作者是中国人,清华毕业,后来又在美国的一所大学取得了博士学位。在Google干过,后来又去了腾讯,现在不知道他身在何处。这本书是从IT行业来讲解数学上的知识的,用到最多的是概率论和统计学,还有线性代数。现在忽然想写写读后感发现竟然不知道如何来说我的感受了。其实在阅读期间,每读完一些章节的时候自己的感触和感受也都挺多的。这样吧,我就再去翻看一下目录,之后来讲述讲述哪些章节让我感触深刻,或者是一句话,或者是一种技术。 题献我首先来说说题献。数学之美的题献是这样的,如下:本书谨献给我的家人。愿科学之精神在国民中得到普及,愿中国年轻的一代涌现出更多杰出的专业人才。其实我是很喜欢题献的,我觉得是作者对他人的感谢,以及作者自己写书的希望。我还记得在读Thinking In Java的时候的题献是这样的,献给那些正在努力创造更伟大的下一代计算机语言的人们。每次读到这样有鼓舞力量的语言,我的内心都很激动。 文字和信息这是全书的第一章,介绍了语言和文字的发展过程,之后又引出了数字和信息的概念,并且说出了这些概念的发展。从这一点我明白到了一件事情,有的事情真的不是我们发明的,而是随着我们的发展,有些东西是自然而然的出现的,或者说是我们认知到了,也可是说是我们发现的,但事物的本质一直就在那里,没有任何变化,我想表达的这一点,我也表达不太清楚。也可能是我理解错误了,但我又想读书吗,一千个人有一千个看法。 图论和网络爬虫对这一章印象深刻是因为图论和爬虫这两件事情。首先我最近在刷一些算法上的题,我在算法导论中也看到过图,所以我对应该是比较熟悉的。而后是爬虫,在15年那会我就听说过爬虫,但是具体干什么也不太清楚。后来随着这词越来越火,我看过一次关于爬虫的技术教程,教我们在腾讯某个新闻网站上下载一些新闻内容,具体的操作是使用Python这门语言进行的。这次看了爬虫,没想到竟然和图论有关,这一点让我有点吃惊也有些开心。我原来以为图就是能求一求最短路径了、最小生成树这些,没想到竟然也有这么高端的应用了。其实也谈不上高端,只是我个人比较感兴趣。作者介绍了说,网页中的链接就像是图中的边,网页就像是节点,当然网页的内容也就是说的节点中带的有用的信息了。之后作者又介绍了对于特定的信息,该如何进行爬取,是使用BFS还是使用DFS。这是一个没有标准答案的问题,却揭示BFS和DFS的不同性质,而这种性质却能用在不同的场景中。这一点作为一个学生来说,BFS和DFS都是用来遍历图的,只要学会一种就可以了,这种思想是不对的。 余弦定理和新闻分类关于这一章我还发了说说,我把说说的内容贴过来。余弦和卡列尼娜。幸福的家庭都是相似的,不幸的家庭各有各的不幸。这句话是卡列尼娜开篇的一句话,全书也是围绕着这句话展开讲诉的。余弦就是中学课本中的cos,描述着三角形三边长度与余弦值的数学公式。相似和余弦角度。用过淘宝这个软件的朋友可能会有过这样的体验,通常在购物车的最下方会有一个猜你喜欢的提示,你买过的东西或是加入购物车的东西和猜你喜欢中提示的东西是相似的。余弦的角度由0到90度,如果把其中两条边当作是向量,余弦的值就可以通过向量的内积除以向量的模得到。如果把你买的东西进行量化就是一个向量,把其他人买的东西进行量化就是一堆向量,之后就去这一堆人中找于你最相似的人,余弦的角度越小代表越相似,最后把于你最相似的人买的东西放在了你的猜你喜欢中。与你购物车一样的那些购物车往往都是相似的,与你购物车不一样的那些购物车各有各的不同。这并不是发明,因为人本来如此,物以类聚,人以群分。当时对这个问题思考了很久,主要的原因是自己觉得余弦这个数学概念是容易明白的,其次是自己之前也写过一个关于分类相关的问题。透过这个问题我在想,后数据时代会是什么样子,也就是我四五十岁后这世界会变成什么样子。 暗算和RSA读到这里的时候,我对RSA的加密方法感到震惊,加密方法是及其简单的,第一步是找两个很大的质数P和Q,然后计算他们的乘积N = P x Q,M = (P - 1) x (Q - 1);第二步是找一个整数E,使得M和E互质;第三步找一个整数D,使得E x D除以M余1,即E x D mod M = 1。经过这三个步骤加密系统就设计完好了,其中E是公匙,D是密匙。比如要对x这个数字进行加密则进行这样的运算X^E mod N = Y,其中Y就是密文,解密运算是Y^D mod N = X。我惊叹如此简单的操作,在现代这么发达的社会中,破解这样的密码则需要几十年的时间,就算你使用成千上万的服务器一起来计算,这一点让我感叹数学的伟大。后来因为作者提到了这部电视剧暗算,我就去把电视剧看完了,电视剧中规中矩,讲了很多,演员的表演也不错。 谈谈数学模型的重要性这一章中作者没有去聊计算机的事情,而是聊了天文学中的故事。我觉得这并不是在聊事务的表面,而是想告诉我们解决问题的方法。对于作者谈到的解决那种很大规模的问题,比如地心说和日心说这样的问题,我感觉到是庞大的。我就谈谈我在解决编程问题时候经过,刚开始编程的时候大致就是去背,背这个字对中国学生来说应该是最熟悉的,现在想来背是最笨的方法,背的原因也就是不明白,才会有死记硬背这个词。后来编程解决问题的过程就成了找相似,看到这个题目我去想是跟我哪个背过得相似,相似的话就去套,如果不相似就再去找相似,真的找不出来就去查看答案,接着背。这样的学习持续了很长时间,后来我才开始思考。比如看到一个题目,我不在先打开电脑,写个helloworld后,再去思考,而是一开始就开始在那思考,当把问题想明白后再去编码实现。我之前也会去思考但并不是这样的,而是一边写代码一边思考,大多数的时候代码都白白写了,因为问题没想明白之前,代码也得修修改改。当去思考一个要解决的问题,我常常会去演绎,比如举出一些例子,但我的思维总会漏掉一些例子,这也就是思考不全面。继而就是从演绎的这些例子中去总结一个规律,我觉得这个是蛮难得,因为这个是一瞬间的事情,你看的到也就看的到了,而你看不到那也就看不到了。这样讲是有些天赋的意思,但思考这回事,真的同智商是有关系的。我记得在我幼儿园的时候,父亲曾经给我出过一道益智题,当然是很难得,题目我记得不太情况了,是根据文字来描述数学问题,从而去文字的规律中找出解题的答案,我记得答案是当年的年份,好像是2003。那个问题我想了很久,也讲给很多同学说,大家都觉得太难了,然而我却一直在思考,直到有一天想出了答案,就去父亲那边核对是否正确,得到了肯定的答复。后来我就觉得自己不是太笨的人,而现在去看这个问题,我觉得解题的关键是坚持!我坚信自己能解答出来,并且不放弃一直去思考,关于这样的经历我还是有过,是在初中的时候去解决一个关于三角形的问题,那会我每天都在思考,洗脸时候闭上眼睛那个题的过程就在大脑中演绎着。后来这种经历就越来越少了,因为要面对考试这件事情,我们没有太多时间去考虑为什么这样,而仅仅知道就是这样就可以了。至今我那些考取研究生的同学也是这样,我们常常讨论着讨论着就会得出这样的结论,考试的答案是怎么我们就怎么,其实得出这样的结论的时候也反应出出题人对概念本质的理解也是片面的,所以才会出一个颇具争议的题目。在后来章节的介绍中,作者列举了两种思考类型,第一种是比较繁复的,这种感觉就像是打补丁,说的通俗点就有点治标不治本的感觉,因为他找不到本质,只能就这现实情况去解决,其中他也列举出了这样的伟人,说的是在自然语言处理方面的顶尖人物:柯林斯。当然说这位伟人的时候就不能用找不到问题的本质了,因为柯林斯也具有数学模型抽象的功力,他把问题变得复杂的原因是因为他追求完美,每个细节都会考虑到。第二种是比较简单的,作者也认为这是他的追求,就是通常复杂的问题的解法是简单的也是大家轻易可以明白的,这就是大家常说的简单哲学吧。我回想起小时候以及我现在去解决问题通过是复杂的,因为我很难看到问题的本质。我记得小学时候在解决一个矩形边长的问题,我解决完后,把题目拿给老师看,后来我父亲也看到了我的解法,他可能只用1个式子就写完了,而我的解法却写了4到5行。后来老师评价我说,这说明他的确去思考问题了,只是他的思考是复杂的,当父亲给我说了他的解法时我也是能明白的。所以我也想在此告诫自己,思考的重要性,以及希望我同之前一样,不要放弃去思考。相信自己,并且付诸行动。 大数据的威力也是很早就听过大数据了,具体记不得是什么时候了。关于数据,2014年那会学数据库,第一次听到它形式化的定义,是事实或观察的结果,是对客观事物的逻辑归纳,是用于表示客观事物的未经加工的的原始素材。有幸那年在老师的带领下,做了一个进销存的项目,其中关于数据库的是由我来负责的,主要的工作是写写SQL语句。那会透着那些查询,我慢慢的熟悉了业务流程,感觉数据真是蛮强大,这个点该如何解释呢,就我当时的认识,最直观的感受就是,数据真的可以描述这个世界。在作者的文章中他刚开始的时候也描述了数据,说现在互联网上的一切信息都可以用数据来表达。后来作者谈到了大数据,作者之前也聊过这个话题,是在聊分布式计算的时候聊到的。他接着列举了那些机器学习的框架在以前的应用,以及现在的应用。作者从他的工作上,给出了如果有了大数据,关于搜索的事情可以变得更出色。其实我觉得把大数据的发生说成是由分布式来推进的,这点对于我来说受益匪浅。大数据的大其实就是说计算复杂,也就是计算量超级大,作者用了PB级别量来说明这个量到底有多大。然而分布式就解决了这样的问题,分布式的原理其实就是大伙一起干,比如遇到一个问题,并不是说由一台计算机去完成,而是有成千上万台计算机一起完成,这也就是媒体上常常提到的云计算。其实这个问题的本质其实是分而治之,分治是算法领域的一个重要的思想。有一次我同一个同学解释什么是大数据,我就对他说,你别觉得大数据多么高端,遥不可及的,其实就是用了统计学里的知识来分析数据罢了。现在觉得我这么说是不完整的,因为我没有提到分布式这个概念。今年在学校的时候有一位老师让我帮忙写一个分类算法,是关于图书推荐的算法。当时我查阅了相关资料,发现其实就是用到了上面提到的余弦的事情,然而那个斜率的算法也不用你去写,我用了Python中scipy这个库,就直接带有了斜率计算的公式。后来老师让我在hadoop平台上写出来,这样才能实现分布式。后来由于其他事情我也没有去完成在hadoop平台上的代码。作者聊到google现在的医疗团队正在使用大数据来解决癌症的事情,读到这里我也是蛮激动的,如果真能实现,那将会是一件多么有意义的事情! 总结有一次自己喝醉了酒,想起了《数学之美》这本书,去各大社交平台搜索了吴军作者,在微博平台上搜索到了他。我就着醉意表达了我对他的敬佩。读完这本书后,我知道了自己在数学上的不足之处,以及在解决问题时候的思维的差异。在以后的道路上我会努力让自己变得更好。","tags":[{"name":"读书","slug":"读书","permalink":"http://turingxi.top/tags/读书/"}]},{"title":"1016.Phone Bills","date":"2017-12-01T16:00:00.000Z","path":"2017/12/02/1016-Phone-Bills/","text":"英文描述Phone Bills (25) A long-distance telephone company charges its customers by the following rules:Making a long-distance call costs a certain amount per minute, depending on the time of day when the call is made. When a customer starts connecting a long-distance call, the time will be recorded, and so will be the time when the customer hangs up the phone. Every calendar month, a bill is sent to the customer for each minute called (at a rate determined by the time of day). Your job is to prepare the bills for each month, given a set of phone call records.Input Specification:Each input file contains one test case. Each case has two parts: the rate structure, and the phone call records.The rate structure consists of a line with 24 non-negative integers denoting the toll (cents/minute) from 00:00 - 01:00, the toll from 01:00 - 02:00, and so on for each hour in the day.The next line contains a positive number N (<= 1000), followed by N lines of records. Each phone call record consists of the name of the customer (string of up to 20 characters without space), the time and date (mm:dd:hh:mm), and the word “on-line” or “off-line”.For each test case, all dates will be within a single month. Each “on-line” record is paired with the chronologically next record for the same customer provided it is an “off-line” record. Any “on-line” records that are not paired with an “off-line” record are ignored, as are “off-line” records not paired with an “on-line” record. It is guaranteed that at least one call is well paired in the input. You may assume that no two records for the same customer have the same time. Times are recorded using a 24-hour clock.Output Specification:For each test case, you must print a phone bill for each customer.Bills must be printed in alphabetical order of customers’ names. For each customer, first print in a line the name of the customer and the month of the bill in the format shown by the sample. Then for each time period of a call, print in one line the beginning and ending time and date (dd:hh:mm), the lasting time (in minute) and the charge of the call. The calls must be listed in chronological order. Finally, print the total charge for the month in the format shown by the sample.Sample Input:10 10 10 10 10 10 20 20 20 15 15 15 15 15 15 15 20 30 20 15 15 10 10 1010CYLL 01:01:06:01 on-lineCYLL 01:28:16:05 off-lineCYJJ 01:01:07:00 off-lineCYLL 01:01:08:03 off-lineCYJJ 01:01:05:59 on-lineaaa 01:01:01:03 on-lineaaa 01:02:00:01 on-lineCYLL 01:28:15:41 on-lineaaa 01:05:02:24 on-lineaaa 01:04:23:59 off-lineSample Output:CYJJ 0101:05:59 01:07:00 61 $12.10Total amount: $12.10CYLL 0101:06:01 01:08:03 122 $24.4028:15:41 28:16:05 24 $3.85Total amount: $28.25aaa 0102:00:01 04:23:59 4318 $638.80Total amount: $638.80 中文描述电话账单一个长途电话公司的收费规则如下:一个长途电话的消费是根据每分钟的价钱来定计算的,这价钱依赖于一天中的不同时段。当一个顾客开始打电话的时候,这个时间会被电话公司记录,当顾客挂断电话时,这个时间也会被记录。每个月,带有每段通话记录的账单将会发送给客户,其中每段通话是根据一天中不同时间段来计算的。你的工作是准备每月的账单和手机通话记录的一个集合。输入规则:每个输入文件包含一个测试用例。每个用例有两个部分:比率结构体和手机通话记录。比率结构体是指在一行内有24个非负数的整数,这24个整数代表着长途电话费。第一个数字代表从00:00到01:00的电话费,第二个数字代表从01:00到02:00的电话费,依次类推。第24个数字就代表着23:00到00:00的电话费。这个包含关系好像没有说清楚,比如一个人从00:30开始打电话,打到了01:22。那么我想问其中的01:00算第一个数字的费用还是算第二个数字的费用?下一行包含一个正数N(<= 1000),接着是N条记录。每条记录包含一个顾客的名字(由20个字符组成不包含空格),一个时间和日期(mm:dd:hh:mm),一个单词:在线或者下线。每个测试用例,所有的数据都在一个月中。每个在线记录和下线记录是按照时间先后顺序成对出现的。任何一个没有对应下线记录的在线记录都将被忽略,任何一个没有对应在线记录的下线记录也会被忽略。输入保证至少有一对在线记录和下线记录是对应的。你可以假设在同一时间没有任何两条记录是相同的。时间采用24小时制。输出规则:对于每个测试用例,你必须每个顾客的手机账单。打印账单必须按照顾客名字的字母顺序进行打印(大写在小写)。对于每个顾客,在第一行内输出顾客名字和月份,格式如输出样例。之后打印每个时间段的电话,在一行内打印开始和结束时间和日期(dd:hh:mm),最后打印时间(分钟)和收费。没通电话必须按照时间先后顺序排序(这里默认为通话时长的递减顺序)。最后一行打印每月的总收费,格式如样例输出。样例输入:10 10 10 10 10 10 20 20 20 15 15 15 15 15 15 15 20 30 20 15 15 10 10 1010CYLL 01:01:06:01 on-lineCYLL 01:28:16:05 off-lineCYJJ 01:01:07:00 off-lineCYLL 01:01:08:03 off-lineCYJJ 01:01:05:59 on-lineaaa 01:01:01:03 on-lineaaa 01:02:00:01 on-lineCYLL 01:28:15:41 on-lineaaa 01:05:02:24 on-lineaaa 01:04:23:59 off-line样例输出:CYJJ 0101:05:59 01:07:00 61 $12.10Total amount: $12.10CYLL 0101:06:01 01:08:03 122 $24.4028:15:41 28:16:05 24 $3.85Total amount: $28.25aaa 0102:00:01 04:23:59 4318 $638.80Total amount: $638.80","tags":[{"name":"PAT","slug":"PAT","permalink":"http://turingxi.top/tags/PAT/"},{"name":"C","slug":"C","permalink":"http://turingxi.top/tags/C/"}]},{"title":"1015.Reversible Primes","date":"2017-11-30T16:00:00.000Z","path":"2017/12/01/1015-Reversible-Primes/","text":"英文描述Reversible Primes (20) A reversible prime in any number system is a prime whose “reverse” in that number system is also a prime. For example in the decimal system 73 is a reversible prime because its reverse 37 is also a prime.Now given any two positive integers N (< 10^5) and D (1 < D <= 10), you are supposed to tell if N is a reversible prime with radix D.Input Specification:The input file consists of several test cases. Each case occupies a line which contains two integers N and D. The input is finished by a negative N.Output Specification:For each test case, print in one line “Yes” if N is a reversible prime with radix D, or “No” if not.Sample Input:73 1023 223 10-2Sample Output:YesYesNo 中文描述反转素数 在任何数字系统中一个可以反转的素数是指这个数字在那个系统中通过“反转”也是一个素数。举个例子,一个十进制系统中的一个数字73是一个可以反转的素数,因为他的反转数字37也是一个素数。现在有任意两个正整数N(< 105)和D(1 < D <= 10),你要求得这个这个正数N是否是D进制的反转数。输入规格:一个输入文件包含一些测试用例。每个测试用例在一行内包含两个整数N和D,当输入的N为负数时,一位这个测试文件结束了。输出规格:对于每个测试用例,如果N是一个在D进制中可以反转的素数就输出“Yes”否则就输出“No”样例输入:73 1023 223 10-2样例输出:YesYesNo 分析与解题1 输入的数字是一些十进制整数,也就是说不一定是素数,故先判断是否为素数2 如果是素数,之后翻转这个数字,之后计算翻转后的数字是不是也是素数3 翻转。题目给出的N是十进制数字,故先要把十进制数字转换为D进制数字。通过十进制变化为D进制数字的方法为:除D取余法(此处不再详细讨论)。得到的结果是一个翻转后的D进制数。再通过D进制数转换为十进制数的方法,D进制的各个位数乘上D的位置次方后再相加。D进制转换为10进制,从高位开始或者从低位开始都可以,但要需要记住D进制数字的位数。4 最后判断翻转后的十进制数字是否为素数。ps:输出格式,之前都是将所有的输出存放在数组中,通过判断数值的标志值来输出,这次看了ZJU上的代码,发现可以判断一个输出一个。在人工调试的时候,这样的输出是伴随着输入一个就输出一个进行执行的,起初以为这样是违规的,这次试验后,发现也是可以通过的,所以之后可以看情况来采用这样的方法来进行输出。关于分析与代码的构造,看了ZJU的代码,发现代码的构造条理清晰明了,这点值得学习。期间有一个点没有通过,后来调试后,发现是结构问题,并不是解决主要问题的方法,比如判断是否为素数以及十进制转D进制出了问题,是在输入格式上出现了错误,自己看了ZJU的代码后,有意识的朝着它的代码去更改,最后虽然与ZJU的代码不同,但是我发觉那个没过去的点已经通过了,所以我想问题是出在了输入结构那边。 C语言实现12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758#include<stdio.h>#include<math.h>int y[64] = {0};int isprime(int p){ if(p == 0 || p == 1) return 0; int i = 2; while(i < p) { if((p % i) == 0) return 0; i++; } return 1;}int main(){ int n, d; while(1) { scanf(\"%d\", &n); if(n < 0) break; else scanf(\"%d\", &d); if(isprime(n) == 1) { //反转 int j = 0; while(n != 0) { y[j] = n % d; n /= d; j++; } //将反转数字转换为十进制数字 int k = 0; int sum = 0; j--; while(j >= 0) { sum += y[k] * (int)(pow(d, j)); j--; k++; } //判断是否为素数 if(isprime(sum) == 1) puts(\"Yes\"); else puts(\"No\"); } else puts(\"No\"); } return 0;} 测试链接click me","tags":[{"name":"PAT","slug":"PAT","permalink":"http://turingxi.top/tags/PAT/"},{"name":"C","slug":"C","permalink":"http://turingxi.top/tags/C/"}]},{"title":"1014.Waiting in Line","date":"2017-11-09T16:00:00.000Z","path":"2017/11/10/WitingLine/","text":"英文描述Waiting in Line (30) Suppose a bank has N windows open for service. There is a yellow line in front of the windows which divides the waiting area into two parts. The rules for the customers to wait in line are:1 The space inside the yellow line in front of each window is enough to contain a line with M customers. Hence when all the N lines are full, all the customers after (and including) the (NM+1)st one will have to wait in a line behind the yellow line.2 Each customer will choose the shortest line to wait in when crossing the yellow line. If there are two or more lines with the same length, the customer will always choose the window with the smallest number.3 Customer[i] will take T[i] minutes to have his/her transaction processed.4 The first N customers are assumed to be served at 8:00am.Now given the processing time of each customer, you are supposed to tell the exact time at which a customer has his/her business done.For example, suppose that a bank has 2 windows and each window may have 2 custmers waiting inside the yellow line. There are 5 customers waiting with transactions taking 1, 2, 6, 4 and 3 minutes, respectively. At 08:00 in the morning, customer1 is served at window1 while customer2 is served at window2. Customer3 will wait in front of window1 and customer4 will wait in front of window2. Customer5 will wait behind the yellow line.At 08:01, customer1 is done and customer5 enters the line in front of window1 since that line seems shorter now. Customer2 will leave at 08:02, customer4 at 08:06, customer3 at 08:07, and finally customer5 at 08:10.Input Specification:Each input file contains one test case. Each case starts with a line containing 4 positive integers: N (<=20, number of windows), M (<=10, the maximum capacity of each line inside the yellow line), K (<=1000, number of customers), and Q (<=1000, number of customer queries).The next line contains K positive integers, which are the processing time of the K customers.The last line contains Q positive integers, which represent the customers who are asking about the time they can have their transactions done. The customers are numbered from 1 to K.Output Specification:For each of the Q customers, print in one line the time at which his/her transaction is finished, in the format HH:MM where HH is in [08, 17] and MM is in [00, 59]. Note that since the bank is closed everyday after 17:00, for those customers who cannot be served before 17:00, you must output “Sorry” instead.Sample Input:2 2 7 51 2 6 4 3 534 23 4 5 6 7Sample Output:08:0708:0608:1017:00Sorry 中文描述通道等待 假设一个银行有N个服务窗口。窗口前的一条黄通道将等待区域分成两个部分。顾客的等待规则如下:1 在每个窗口前黄色通道里面的空间足够容纳这个通道上的M个顾客。因此当N条通道占满了人的时候,NM + 1的那个顾客将会站在黄线后的区域等待。2 当每个顾客在黄色通道后等待时,他们将会去选择最短的通道进入。如果有2条或多条通道具有相同的长度是,总是选择窗口号最小的通道。3 顾客i需要t(i)分钟去办理他的业务。4 银行早上8点开始上班。5 现在给出每位顾客需要办理业务的时间,你要求出每位顾客办完他的业务时的时间。举一个例子,假设这个银行有2个窗口,每个窗口可以容纳2个顾客在黄线里面等待着。这里有5个顾客办理业务的时间是1,2,6,4和3分钟。在早上8点,顾客1在窗口1办理业务,顾客2在窗口2办理业务,顾客3在窗口1前等待,顾客4在窗口2等待。顾客5在黄线外等。在08:01分,顾客1办理完业务,并且顾客5就去窗口1千等待了,窗口1那个通道里的人最少。顾客2在08:02办理完业务,顾客4在08:06办理完业务,顾客3在08:07办理完业务,顾客5在08:10办理完业务。输入规格:每个文件包含一个测试用例。每个用例的第一行包含4个正整数:N(<= 20,窗口数),M(<= 10,窗口容量数),K(<= 1000,顾客数量),Q(<= 1000,需要查询这些顾客几点办理完业务)下一行包含K个正整数,每个整数代表每个顾客需要办理业务的时间。最后一行包含Q个正整数,是我们要求得这些顾客啥时候办理完业务。每个顾客被标记为1到N。输出规格:对于要查询的Q个用户,在一行内打印他们办理完业务时候的时间,格式是:小时:分钟,小时从08到17,分钟从00到59。注意,银行为客户提供服务的时间在17:00点之前,如果17:00之后还有客户需要我们服务,我们只能对他说“Sorry”样例输入:2 2 7 51 2 6 4 3 534 23 4 5 6 7样例输出:08:0708:0608:1017:00 分析与解题总算把这题读明白了,花了快20分钟。题意是否忽略了排队时间。假如早8点大家全都到了,有20个窗口,每个窗口容纳10人,那么前200个人是不是1秒就排好队了,其他800人在黄线外等着,等着去选择队伍最短的。还是只有10个人去排队,剩下的990个人在哪等着,去动态的选择队伍最短的。按道理讲显示中应该是我理解的那样,但从给的那个例子以及题目中要求好像不是这样的,也就是队伍有空你就进去。只有在黄线外的那些顾客采取动态选择队伍最短的进去。 此刻代码已经写了出来,过了2个点,得了16分,画了快两天的时间。昨天看了别人的代码和思路,大致跟我的是一样的。所以我想在大体思路上是没有什么问题了,剩下的应该就是细节的处理问题了。我在这里通过文字把思路整体撸一遍,通过10个队列来模拟排队方式,初始化的时候,将NM个人,放入队列中,第一个人在1队列1号位置,第二个人在2队列1号位置,依次类推。这一点我是使用了队号和位置号来确定是第几个人的,假如是i队列和m位置号,那也就是第(i – 1)* n + j个人所在位置。我看了其他人的代码有通过直接去余的方法来写,觉得是蛮简单的。第i个人就在第i % N的位置上,次数他并没有去考虑第几个队列和第几个位置,因为i是一直在增加的,所以这样去初始化队列是蛮方便的。初始化时要考虑一件事情,k(所有顾客)是否小于nm,所以在初始化的时候要进行判断,我使用的方法是在入队处去统计人数,当k是小于nm时候,如果统计的人数= nm时候就会自动结束的。我刚才去测试了边界情况,假使所有的值都是大于或者等于1的。之后就开始判断哪个队列的队头顾客先把业务办理完毕,当队头顾客业务办理完毕后,将此顾客对应的是第几号以及他所花费的时间累加其起来,算是他的结束时间。这一点在实现的时候颇麻烦的,啰里啰嗦的,可能是我没有设计好。我在这里把我的思路叙述一遍,我的队列里标记了顾客是第几个顾客以及他办理业务所花费的时间,字段名称是c和time,当时也没有想到较好的英文,c就是count的意思,计数的意思。这个队列设计的也是颇啰嗦,感觉就是没有设计感,是想到哪就搞到哪,我一直在纠结,出队与入队的操作,以及返回值的问题,我是在想返回结果体也就是element还是time字段,如果是element的话那么和它的出队参数将不一致,后来也没有想这个问题,就放到那了。当初始化完成后,我就开始找队头用时最少的顾客,我找的一定是一个对号最小且用时最少的顾客,之后我就返回这个顾客所在的队列以及这个顾客是第几个顾客,我是第几个顾客,我存储在了c中。整体循环我使用的是for i = 1 to k,也就是对这k个用户要进行一次出队和入队操作,之后分别统计他们的结束时间。之后有一点要谈的是,也就是银行的服务时间,银行在8:00到17:00之间接受服务请求,也就是当你17:00到的时候,哪怕你的服务时间是10个小时那,银行也会对你进行服务,但是当超过了17:00点,你就不能去申请服务了,对于这样的顾客,我们应该对他说sorry,明天再来吧。我在处理这一点的时候,是为每个队列添加了一个计时器,计时器的初值为0。现在又详细考虑了这件事情,感觉不行了,漏洞是在太多了。在思路上有一个大的点没有考虑到,需要改进一下思路。 昨天又去把代码改进了一番,整体的思路是所有顾客都要入队,当然所有顾客也都要出队,后来又思索了很多,最后剩下一个点没过,报的错误是段错误,昨天晚上也没有很好的想明白这个事情。今天早上起来,我觉得去读一读ZJU的代码,看看两者的思路中的差异到底在哪。因为ZJU的代码是cpp写的,所以会记录一番关于cpp的相关知识。刚才去看了ZJU的代码,思路清晰,代码也简洁,整体的思路跟我的是一样的,只不过我的实现看起来复杂太多了。 我决定还是放弃了,按照ZJU的思路去写一遍吧。说不定能发现自己的错误到底在哪。调试到要抓狂的阶段了,真的是找不出来是哪里错了。按照ZJU的方法写了一遍,又是一堆的错误。pass吧,这道题。 C语言实现123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164#include<stdio.h>typedef struct{ int time; int c;}elem;typedef struct{ elem qlen[30]; int rear; int front; int length;}queue;queue win[30];int info[2001];int query[2001];int end[2001];int accumulate[30] = {0};int n, m, k, q;void initqueue(){ int i = 1; while(i <= n) { accumulate[i] = 0; win[i].front = win[i].rear = 0; win[i].length = 0; i++; }}void enqueue(int i, elem node){ if(win[i].length < m) { win[i].qlen[win[i].rear] = node; win[i].rear = (win[i].rear + 1) % m; win[i].length++; }}elem show(int i){ return win[i].qlen[win[i].front];}void dequeue(int i){ if(win[i].length > 0) { win[i].front = (win[i].front + 1) % m; win[i].length--; } }void input(){ scanf(\"%d%d%d%d\", &n, &m, &k, &q); int i = 1; while(i <= k) { scanf(\"%d\", &info[i]); i++; } i = 1; while(i <= q) { scanf(\"%d\", &query[i]); i++; }}elem min(int *num){ elem m; m.time = 9999999; m.c = -1; int i = 1; while(i <= n) { //统计队列不为空的最小值 if(win[i].length > 0) { if(m.time > show(i).time) { m = show(i); *num = i; } } i++; } return m;}void calc(){ int en = 0, de = 0; int i = 1; int count = 1; while(i <= m) { int j = 1; while(j <= n) { elem tmp; tmp.c = (i - 1) * n + j; tmp.time = info[(i - 1) * n + j]; if(count <= k) { en++; enqueue(j, tmp); count++; } j++; } i++; } int num; //窗口号 elem tmp = min(&num); while(tmp.c != -1) { tmp = min(&num); if(accumulate[num] >= 540) end[tmp.c] = -1; else { end[tmp.c] = tmp.time + accumulate[num]; accumulate[num] = end[tmp.c]; } dequeue(num); de++; if(count <= k) { elem enq; enq.time = info[count]; enq.c = count; en++; enqueue(num, enq); count++; } }}void formate(){ int i = 1; while(i <= q) { if(end[query[i]] != -1) { int hour,minutes; hour = 8 + end[query[i]] / 60; minutes = end[query[i]] % 60; printf(\"%02d:%02d\\n\", hour, minutes); } else puts(\"Sorry\"); i++; }}int main(){ input(); initqueue(); calc(); formate(); return 0;} 测试链接click me","tags":[{"name":"PAT","slug":"PAT","permalink":"http://turingxi.top/tags/PAT/"},{"name":"C","slug":"C","permalink":"http://turingxi.top/tags/C/"}]},{"title":"1011.World Cup Betting","date":"2017-11-05T16:00:00.000Z","path":"2017/11/06/WordCupBetting/","text":"英文描述World Cup Betting (20) With the 2010 FIFA World Cup running, football fans the world over were becoming increasingly excited as the best players from the best teams doing battles for the World Cup trophy in South Africa. Similarly, football betting fans were putting their money where their mouths were, by laying all manner of World Cup bets.Chinese Football Lottery provided a “Triple Winning” game. The rule of winning was simple: first select any three of the games. Then for each selected game, bet on one of the three possible results – namely W for win, T for tie, and L for lose. There was an odd assigned to each result. The winner’s odd would be the product of the three odds times 65%.For example, 3 games’ odds are given as the following: W T L1.1 2.5 1.71.2 3.0 1.64.1 1.2 1.1To obtain the maximum profit, one must buy W for the 3rd game, T for the 2nd game, and T for the 1st game. If each bet takes 2 yuans, then the maximum profit would be (4.1 * 3.0 * 2.5 * 65%-1) * 2 = 37.98 yuans (accurate up to 2 decimal places).Input Specification:Each input file contains one test case. Each case contains the betting information of 3 games. Each game occupies a line with three distinct odds corresponding to W, T and L.Output Specification:For each test case, print in one line the best bet of each game, and the maximum profit accurate up to 2 decimal places. The characters and the number must be separated by one space.Sample Input:1.1 2.5 1.71.2 3.0 1.64.1 1.2 1.1Sample Output:T T W 37.98 中文描述世界杯赌博 伴随着2010世界杯的开赛,在南非,来自于最好的球队的球员在为世界杯奋战时来自世界各地的球迷也变得越来越兴奋。同样的,赌球运动也开始了。中国足球博彩会提供一个“三倍赢”游戏。胜利的规则很简单:首先选择任何三场比赛。然后对于每个选择的游戏,在三个选项中赌一个可能的结果,其中W代表赢,T代表平局,L代表失败。对于每个结果是赌注赔率的分配。胜利者的3个赌注赔率将相乘最后在乘上65%。为了获得最大利润,从第三场游戏中买W,第二场游戏中买T,第一场游戏中买T,如果每注2元,那么最大利润就是(4.1 * 3.0 * 2.5 * 65%-1) * 2 = 37.98元(精确到两位小数)。输入规格:每个输入文件包含一个测试用例。每个用例包含3场游戏的赌博信息。每场游戏有三个赌注赔率相当于W、T和L。输出规格:对于每个测试用例,在一行内输出每场游戏赌注最大的值和最大的利润,小数点精确到两位。字符和数字之间用空格间隔。样例输入:1.1 2.5 1.71.2 3.0 1.64.1 1.2 1.1样例输出:T T W 37.98 分析与解题第一次做的时候有点紧张,主要是题没读懂,就算现在,题意也没有很清楚,可能是关于博彩行业的事情,自己对这些东西又不是特别懂。当你把题读明白后,会发现这根本不是算法题了,基本的输出输出题。可是从另一个方面也反应出我的英语是有些差了,今天看了一会英语四级,只看了3个听力的新闻播报我就快被折磨的不行了。心里很down,可还是要加油!不气馁,不骄傲。我是通过9个数字的来分布标记W、T和L的,后来看到他们写的弄了一个数组来标记,这方法比较简单下次可取。 最后说一点关于浮点数的事情,浮点数有时候是很不精确的,不论精确到第几位。比如你算0.1 mul 0.2,结果是0.0200000004。先写一点注意事项:用double就用lf,用float就用f,我说的是格式控制符,也就是说double a,输出或者输出a的时候都要scanf(“%lf”, &a)或是printf(“%lf”, a)。 C语言实现1234567891011121314151617181920212223242526272829303132333435363738394041#include<stdio.h>double info[11];double sum = 0.6500;char sign[4];void input(){ int i = 1; int c = -1; int s = 1; float max = -1.1111; while(i <= 9) { scanf(\"%lf\", &info[i]); if(max < info[i]) { max = info[i]; c = i; } if(i % 3 == 0) { sum *= max; if(c == 1 || c == 4 || c == 7) sign[s] = 'W'; if(c == 2 || c == 5 || c == 8) sign[s] = 'T'; if(c == 3 || c == 6 || c == 9) sign[s] = 'L'; max = 0; s++; c = -1; } i++; }}int main(){ input(); printf(\"%c %c %c %.2lf\\n\", sign[1], sign[2], sign[3],(sum - 1.00) * 2.00); return 0;} 测试链接click me","tags":[{"name":"PAT","slug":"PAT","permalink":"http://turingxi.top/tags/PAT/"},{"name":"C","slug":"C","permalink":"http://turingxi.top/tags/C/"}]},{"title":"1013.Battle Over Cities","date":"2017-11-05T16:00:00.000Z","path":"2017/11/06/BattleOverRank/","text":"英文描述Battle Over Cities (25) It is vitally important to have all the cities connected by highways in a war. If a city is occupied by the enemy, all the highways from/toward that city are closed. We must know immediately if we need to repair any other highways to keep the rest of the cities connected. Given the map of cities which have all the remaining highways marked, you are supposed to tell the number of highways need to be repaired, quickly.For example, if we have 3 cities and 2 highways connecting city1-city2 and city1-city3. Then if city1 is occupied by the enemy, we must have 1 highway repaired, that is the highway city2-city3.Input Specification:Each input file contains one test case. Each case starts with a line containing 3 numbers N (","tags":[{"name":"PAT","slug":"PAT","permalink":"http://turingxi.top/tags/PAT/"},{"name":"C","slug":"C","permalink":"http://turingxi.top/tags/C/"}]},{"title":"1010. Radix","date":"2017-11-01T16:00:00.000Z","path":"2017/11/02/Radix/","text":"英文描述Radix (25) Given a pair of positive integers, for example, 6 and 110, can this equation 6 = 110 be true? The answer is “yes”, if 6 is a decimal number and 110 is a binary number. Now for any pair of positive integers N1 and N2, your task is to find the radix of one number while that of the other is given.Input Specification:Each input file contains one test case. Each case occupies a line which contains 4 positive integers:N1 N2 tag radixHere N1 and N2 each has no more than 10 digits. A digit is less than its radix and is chosen from the set {0-9, a-z} where 0-9 represent the decimal numbers 0-9, and a-z represent the decimal numbers 10-35. The last number “radix” is the radix of N1 if “tag” is 1, or of N2 if “tag” is 2.Output Specification:For each test case, print in one line the radix of the other number so that the equation N1 = N2 is true. If the equation is impossible, print “Impossible”. If the solution is not unique, output the smallest possible radix.Sample Input:16 110 1 10Sample Output:12Sample Input:21 ab 1 2Sample Output:1Impossible 中文描述进制 给出一对正整数,比如6和110,这个等式6=100是对的吗?答案是肯定的,如果6是一个十进制数且110是一个二进制数。现在对于任何一对正整数N1和N2,你的任务的发现一个数字的进制然而它是根据另一个数字所求得的。输入规格:每个文件包含一个测试用例。每个用例占据一行包含了4个正整数:N1 N2 tag radix这里N1和N2是一个不超过十位数字的正整数。其中每位数是小于它的进制的(比如十进制数中的每一位都是小于10的)并且是来自于集合{0-9,a-z},其中0-9表示十进制数字0-9,a-z代表十进制数字10-35。最后一个数字“radix”,如果“tag”是1就是N1的进制,如果“tag”是2就是N2的进制。输出规格:对于每个测试用例,在一行内输出另一个数字的进制使得N1和N2是相等的。如果找不到使得N1和N2相等的进制的数那就输出“Impossible”。如果存在多个解决方法,那么输出尽可能小的进制。样例输入:16 110 1 10样例输出:12样例输入:21 ab 1 2样例输出:2Impossible 分析与解题昨天读题的时候发现题都没读懂,原因就是radix,这个单词在计算机中的意思是进制的意思。当明白了这个单词的意思,也就大致明白了题意。后来去百度搜索了,一堆人还依然翻译成基数,我就在想你到底有没有学过计算机。今天按照自己的想法去编码,第二次测试的时候已经达到23分。 C语言实现123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128#include<string.h>#include<stdlib.h>#include<stdio.h>#include<math.h>//最大数字20位 1开头long long int result;char n1[11];char n2[11];unsigned long long int tag;unsigned long long int radix;void input(){ scanf(\"%s%s\", n1, n2); scanf(\"%d%d\", &tag, &radix);}void calc(){ //first:找出谁是给出进制的,谁是要求的进制, //令c1是给出的进制,c2是要求得进制 //为什么感觉这了这个有一种很奇异的感觉 char c1[11], c2[11]; if(tag == 1) { strcpy(c1, n1); strcpy(c2, n2); } else { strcpy(c1, n2); strcpy(c2, n1); } //下面所有带1的就是给出进制的数,2的就是要求进制的数 unsigned long long int t1 = 0; //令t1为给出进制的数 unsigned long long int t2 = 0; int len1 = strlen(c1) - 1, len2 = strlen(c2) - 1; //将给出进制的数转化为10进制并赋值给t1 int i = len1; int j = 0; unsigned long long d = 1; while(i >= 0) { int tmp; if(c1[i] >= '0' && c1[i] <= '9') { //数字 tmp = (int)(c1[i] - '0'); } else { //字母 tmp = (int)(c1[i] - 'a' + 10); } t1 += tmp * d; d *= radix; i--; } //come on,开始比较吧 //1. 找到c2总最大的数字,之后从它加1开始计算 i = 0; char max = 0; while(i <= len2) { if(max < c2[i]) max = c2[i]; i++; } //2. 开始从max开始计算 if(max >= '0' && max <= '9') result = max - '0' + 1; else result = max - 'a' + 11; unsigned long long int low = result; unsigned long long int high = (t1 + 1) > result ? (t1 + 1) : result; result = (high + low) / 2; while(1) { i = len2; j = 0; unsigned long long d = 1; t2 = 0; while(i >= 0) { int tem; if(c2[i] >= '0' && c2[i] <= '9') { tem = (int)(c2[i] - '0'); } else { tem = (int)(c2[i] - 'a' + 10); } t2 += tem * d; d *= result; if(t2 > t1) break; i--; } //second if(low > high) { result = -1; break; } if(t1 < t2) { high = result - 1; result = (high + low) / 2; } else if(t1 > t2) { low = result + 1; result = (high + low) / 2; } else break; }}int main(){ input(); calc(); if(result == -1) puts(\"Impossible\"); else printf(\"%ld\\n\", result); return 0;} 测试链接click me","tags":[{"name":"PAT","slug":"PAT","permalink":"http://turingxi.top/tags/PAT/"},{"name":"C","slug":"C","permalink":"http://turingxi.top/tags/C/"}]},{"title":"1009.Product of Polynomials","date":"2017-11-01T16:00:00.000Z","path":"2017/11/02/ProductOfPolynomials/","text":"英文描述Product of Polynomials (25) This time, you are supposed to find A * B where A and B are two polynomialsInput Specification:Each input file contains one test case. Each case occupies 2 lines, and each line contains the information of a polynomial: K N1 aN1 N2 aN2 … NK aNK, where K is the number of nonzero terms in the polynomial, Ni and aNi (i=1, 2, …, K) are the exponents and coefficients, respectively. It is given that 1 <= K <= 10, 0 <= NK < … < N2 < N1 <=1000.Output Specification:For each test case you should output the product of A and B in one line, with the same format as the input. Notice that there must be NO extra space at the end of each line. Please be accurate up to 1 decimal place.Sample Input:2 1 2.4 0 3.22 2 1.5 1 0.5Sample Output:3 3 3.6 2 6.0 1 1.6 中文描述多项式乘积 此时,你应当在多项式A和B中求得A乘B输入规格:每个文件包含一个测试用例。每个用例占据2行,并且每一行包含一个多项式的信息:K N1 aN1 N2 aN2 … NK aNK,在多项式中K是一个非零的数字,Ni 和 aNi (i=1, 2, …, K)是指数和系数。给定1 <= K <= 10, 0 <= NK < … < N2 < N1 <=1000.输出规格:对于每个测试用例你应该在一行内输出A和B的乘积,输出格式和输入格式相同。注意行末没有空格。小数点精确到一位。样例输入:2 1 2.4 0 3.22 2 1.5 1 0.5样例输出:3 3 3.6 2 6.0 1 1.6 分析与解题起初我一直在纠结这种运算是否和真正上的数学运算是相同的,所以总是有些迷惑。当通过后,我发现这种多项式乘积的运算和数学上的乘积运算完全是两回事。但是在这个题中你有得揣摩这个作者自定义的运算,又得去结合实际数学上的运算而考虑到作者没有说的事情,比如在这个题中,作者变没有说不输出系数为零的项。所以这个题在算法上没太多难度,时间全花费在了去揣摩题意。昨天晚上看到凌晨三点钟,终于把自己的代码修改成通过了的。期间也查阅了很多其他人的代码,最简单的方法是打表的方法,速度也最快,当然耗费了额外多的存储空间,我的思路是比较简单的插入和更新策略,但相比打表方法稍微复杂一些。 C语言实现123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122/* A*B,指数相加,系数相乘,系数为0不输出* calc() 如果指数相同就就更新系数的值* 否则就将指数和系数插入,在更新后判断* 系数是否为0,为0就删除,这样的策略是* 存在一个bug的,但题中却通过了,bug* 描述,如果输入:1 1 0和1 2 0这个算法* 会输出:1 3 0.0,此处系数为0,却输出* 关于这点题中也没有明确给出,就如实例* 中所举出的,只有1项,而对于k是输出0* 还是不输出,显然,就这测试结果来讲* 你想怎样就怎样* sort() 计算出来的结果不一定是按照系数* 递减进行输出的,故排个序。*/#include<stdio.h>int ex1[11]; //指数int ex2[11]; //指数double coe1[11]; //系数double coe2[11]; //系数int k1, k2;int exr[101];double coer[101];int c = 1; //最终输出的k,也就是有几项void calc(){ int i = 1; while(i <= k1) { int j = 1; while(j <= k2) { int te; double tc; te = ex1[i] + ex2[j]; tc = coe1[i] * coe2[j]; int k = 1; int f = 0; while(k <= c) { if(te == exr[k]) { f = 1; coer[k] += tc; if(coer[k] == 0) { c--; } break; } k++; } if(f == 0) { exr[c] = te; coer[c] = tc; c++; } j++; } i++; }}void input(){ scanf(\"%d\", &k1); int i = 1; while(i <= k1) { scanf(\"%d\", &ex1[i]); scanf(\"%lf\", &coe1[i]); i++;; } scanf(\"%d\", &k2); i = 1; while(i <= k2) { scanf(\"%d%lf\", &ex2[i], &coe2[i]); i++; }}void output(){ int i = 1; printf(\"%d\", c - 1); while(i <= c - 1) { printf(\" %d %.1lf\", exr[i], coer[i]); i++; }}void sort(){ int i = 1; while(i <= c - 1) { int j = i + 1; while(j <= c - 1) { if(exr[i] < exr[j]) { int t = exr[i]; exr[i] = exr[j]; exr[j] = t; double d = coer[i]; coer[i] = coer[j]; coer[j] = d; } j++; } i++; }}int main(){ input(); calc(); sort(); output(); return 0;} 测试链接click me","tags":[{"name":"PAT","slug":"PAT","permalink":"http://turingxi.top/tags/PAT/"},{"name":"C","slug":"C","permalink":"http://turingxi.top/tags/C/"}]},{"title":"1008. Elevator","date":"2017-10-31T16:00:00.000Z","path":"2017/11/01/Elevator/","text":"英文描述Elevator (20) The highest building in our city has only one elevator. A request list is made up with N positive numbers. The numbers denote at which floors the elevator will stop, in specified order. It costs 6 seconds to move the elevator up one floor, and 4 seconds to move down one floor. The elevator will stay for 5 seconds at each stop. For a given request list, you are to compute the total time spent to fulfill the requests on the list. The elevator is on the 0th floor at the beginning and does not have to return to the ground floor when the requests are fulfilled.Input Specification:Each input file contains one test case. Each case contains a positive integer N, followed by N positive numbers. All the numbers in the input are less than 100.Output Specification:For each test case, print the total time on a single line.Sample Input:3 2 3 1Sample Output:41 中文描述反转素数 在我们的城市中一个高层建筑只有一个电梯。一个请求列表是有N个正数组成。在这列表中,这个数字意味着电梯会在相应的层数停止。电梯需要花费6秒上一层,花费4秒下一层。电梯在每一层停留5秒。对于一个给出的请求列表,你需要计算出电梯在这个列表中实现上上下下所花费的总时间。这个电梯从第0层开始运作,并且完成列表请求的那些层数后不用再回到0层。输入规格:每一个输入包含一个测试用例。每个用例包含一个正整数N,接着是N个正数。所有的数字小于100。输出规格:对于每个测试用例,在一行内输出电梯总运行时间。样例输入:3 2 3 1样例输出:41 分析与解题此题虽然简单,但也不好读懂题意。默认添加一个0进去,一个循环就搞定。判断第一个数和第二个数的大小,第一个数大于第二个数,电梯向下,否则电梯向上,之后进行相应的累加。 C语言实现12345678910111213141516171819202122232425#include<stdio.h>int info[200];int N;int sum = 0;int main(){ scanf(\"%d\", &N); int i = 1, t = 0; while(i <= N) { scanf(\"%d\", &info[i]); if(info[i] > t) { sum += (info[i] - t) * 6 + 5; } else { sum += (t - info[i]) * 4 + 5; } t = info[i]; i++; } printf(\"%d\", sum); return 0;} 测试链接click me","tags":[{"name":"PAT","slug":"PAT","permalink":"http://turingxi.top/tags/PAT/"},{"name":"C","slug":"C","permalink":"http://turingxi.top/tags/C/"}]},{"title":"1007.Maximum Subsequence Sum","date":"2017-10-28T16:00:00.000Z","path":"2017/10/29/MaxinumSubsequence/","text":"英文描述Maximum Subsequence Sum (25) Given a sequence of K integers { N1, N2, …, NK }. A continuous subsequence is defined to be { Ni, Ni+1, …, Nj } where 1 <= i <= j <= K. The Maximum Subsequence is the continuous subsequence which has the largest sum of its elements. For example, given sequence { -2, 11, -4, 13, -5, -2 }, its maximum subsequence is { 11, -4, 13 } with the largest sum being 20.Now you are supposed to find the largest sum, together with the first and the last numbers of the maximum subsequence.Input Specification:Each input file contains one test case. Each case occupies two lines. The first line contains a positive integer K (<= 10000). The second line contains K numbers, separated by a space.Output Specification:For each test case, output in one line the largest sum, together with the first and the last numbers of the maximum subsequence. The numbers must be separated by one space, but there must be no extra space at the end of a line.In case that the maximum subsequence is not unique, output the one with the smallest indices i and j (as shown by the sample case).If all the K numbers are negative, then its maximum sum is defined to be 0, and you are supposed to output the first and the last numbers of the whole sequence.Sample Input:10-10 1 2 3 4 -5 -23 3 7 -21Sample Output:10 1 4 中文描述最大子序列 提供一个K个整数的序列{ N1, N2, …, NK }。一个连续的子序列被定义为{ Ni, Ni+1, …, Nj }其中1 <= i <= j <= K。最大子序列指的是一个连续的子序列中所有的元素之和最大。举一个例子,给出一个序列{ -2, 11, -4, 13, -5, -2 },最大子序列是{ 11, -4, 13 },最大子序列中的和为20。现在你要求得最大子序列的和的最大值,以及在最大子序列中第一个和最后一个数。输入规格:每一个输入文件包含一个测试用例。每个用例占据两行。第一行包含一个正数(K <= 10000)。第二行包含K个数,数与数直接被空格间隔。输出规格:每个测试用例,在一行内输出最大和以及最大子序列中第一个和最后一个数字。这些数字必须以空格间隔,但在行末尾没有空格。在一个用例中,最大子序列不是唯一的,输出下标最小的i和j。如果K个数字都是负数,最大和被定义为0,并且你需要输出整个序列中第一个和最后一个数字。样例输入:10-10 1 2 3 4 -5 -23 3 7 -21样例输出:10 1 4 分析与解题我试着用暴力算法去写了这个,没想到竟然通过了,因此我在怀疑pat那些你只要能想出办法解决的,也不用太去考虑效率的事情,也就是说,你无论使用什么算法,很少会内存溢出或者时间超时。刚才去看了《算法导论》关于这个问题的出处,在第四章分治策略中出现的,刚好那是写了时间测试,暴力和分治的差别还是很大的,尤其在输入规模巨大的时候,当数据量在10w时候,分治是0.01秒,暴力是15秒。刚好这个题是1w,要求是0.4秒,我测了1w中暴力的数据基本在0.3秒和0.2秒之间,所以飘过了。分治的策略的确是很厉害的,这里我在说一遍,分治是把这个最大数组分成2个数组,之后分别求这两个子数组中最大子序列,其实我刚看了《算法导论》中分别求子序列的算法,其实也是基于暴力的,所以我常认为暴力是基本算法,分治的策略却把他分开来算,分别求的2个数组,分治总提到关键的一点,最大子数组必然位于左或右两个数组中或者跨越了这两个数组,他只对其中跨越这两个子数组的问题进行了处理,其余的情况使用了递归进行编写,昨天通过演绎的手法对代码进行了运行,虽然比较麻烦,但也看明白了代码的运行策略。分治这玩意确实很厉害,就在这个求最大子序列的问题上,他的策略的确很牛,我刚刚讲到暴力算法是基础算法,其实并不是的,起码在这个分治算法是不是的,但终其最基本的算法是比较,比较也算不是一个算法,可以说是方案。在此我再来谈谈分治。我还是通过演绎的方法吧,我的水平可能也归纳不来。就好比3,4,5。这个序列,分治的前提推论是最大子序列必然位于:{3}、{5}和{3,4,5}这三个子序列中,之后它开始分别求这3个子序列,因为{3}和{5}这两个子序列的求解同原问题求解相同,这也就是DP中谈到的最优子结构,因为原问题就是求某一个序列的最大子序列,所以{3}和{5}这两个子序列可以递归去求,而第三个子序列{3,4,5},是表明,要求子序列一定是跨越了{3}和{5}这两个子序列,所以最大子序列必然位于{3,4}和{5}这两个子序列之间,这一点我感觉自己也讲不明白,我看了原文中写的是找到{3,4}和{5}这两个子序列的最大子序列之后将其合并,关键就在于这个合并,以及他在寻找{3,4}这个最大子序列时的顺序,是相当迷人的,这个问题你咋一听其实也是寻找{3,4,5}这个序列的最大子序列,但这个问题是加了前提的,前期就是最大子序列必然是跨越了左子序列{3}和右子序列{4}的,因为这个序列就是跨越了左和右的,因为左部最大子序列的值为3下标是1,1,右边最大子序列值为5下标是3,3。因此这个最大子序列是跨越了这两个子序列的,也就是在{3,4,5}这个子序列中的。啰嗦了一堆,我还是举个例子来表明,假如有这样一个子序列{1,2,-4,4,5},这个子序列的最大子序列是在右边的,也就是{4,5}。那么这个子序列也要求的中点为3将这个序列分开的两个子序列{1,2,-4}和{4,5}这两个子序列的最大子序列,之后将其合并,其实我感觉这里的两个子序列的最大子序列是有一些歧义的,因为最大子序列有左和右的说法,这里其实是先求得左,再求得右,之后将其合并,也就是说{1,2,-4,4,5}返回的最大子序列的值是8,下标是1和5,所以这个问题的描述虽然是求得它的最大子序列,但限制的条件是跨越了中点的。因此分治的本质和暴力的思路是不同的,暴力就是穷举,分治是先考虑问题结构,之后与子问题进行连接,之后再原问题与子问题直接找到连接的限制,再去求解问题。 C语言实现12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152#include<stdio.h>int info[10001]; //sequenceint k = 10000;int max = 0;int first; int last;int check = 0; //check为0的话,意味着全是负数void input(){ scanf(\"%d\", &k); int i = 1; while(i <= k) { scanf(\"%d\", &info[i]); if(info[i] >= 0) check = 1; i++; }}void calc(){ int i = 1, j = 1, sum = 0; while(i <= k) { sum = 0; j = i; while(j <= k) { sum = sum + info[j]; if(max < sum) { max = sum; first = i; last = j; } j++; } i++; }}int main(){ input(); if(check == 0) printf(\"%d %d %d\", 0, info[1], info[k]); else { calc(); printf(\"%d %d %d\", max, info[first], info[last]); } return 0;} 测试链接click me","tags":[{"name":"PAT","slug":"PAT","permalink":"http://turingxi.top/tags/PAT/"},{"name":"C","slug":"C","permalink":"http://turingxi.top/tags/C/"}]},{"title":"1006.Sign In and Sign Out","date":"2017-10-27T16:00:00.000Z","path":"2017/10/28/Sigin/","text":"英文描述Sign In and Sign Out (25) At the beginning of every day, the first person who signs in the computer room will unlock the door, and the last one who signs out will lock the door. Given the records of signing in’s and out’s, you are supposed to find the ones who have unlocked and locked the door on that day.Input Specification:Each input file contains one test case. Each case contains the records for one day. The case starts with a positive integer M, which is the total number of records, followed by M lines, each in the format:ID_number Sign_in_time Sign_out_timewhere times are given in the format HH:MM:SS, and ID number is a string with no more than 15 characters.Output Specification:For each test case, output in one line the ID numbers of the persons who have unlocked and locked the door on that day. The two ID numbers must be separated by one space.Note: It is guaranteed that the records are consistent. That is, the sign in time must be earlier than the sign out time for each person, and there are no two persons sign in or out at the same moment.Sample Input:3CS301111 15:30:28 17:00:10SC3021234 08:00:00 11:25:25CS301133 21:45:00 21:58:40Sample Output:SC3021234 CS301133 中文描述登录和退出 在每一天开始的时候,第一个来的人将要打开计算机教室门上的锁,最后一个离开的人将要锁住计算机教室的门。给出一个进门与出门的记录,你要找出那天中谁把门打开了和谁把门锁住了。输入规格:每一个输入文件包含一个测试用例。每个用例包含了一天的记录。这个用例的第一行是一个正整数M,代表一共有多少条记录,接着是M行,每一行的格式为:身份号码 进入时间 出去时间时间的格式为:小时:分钟:秒数,身份号码是一个不超过15个字符的字符串。输出规格:对于每一个测试用例,在一行内输出这一天中开门和关门的人的身份号码,两个身份号码之间需要被一个空格间隔开来。注意:开门和关门的人的记录一定是存在的。每个人的开门时间必须早于关门时间,没有两个人的开门或者关门时间是相同的。样例输入:3CS301111 15:30:28 17:00:10SC3021234 08:00:00 11:25:25CS301133 21:45:00 21:58:40样例输出:SC3021234 CS301133 分析与解题找出最早的登录时间和最晚的退出时间。使用一个二维字符数组来存储这些内容,之后从这个字符数组中解析出ID和登录时间和退出时间,接着对这些时间进行选择,选择最早的登录时间和最晚的退出时间取其ID。想起来挺简单,但写起来是有些复杂的,后来看到strcmp函数可以直接进行判断,用strcmp函数比较简单,其实在输入的时候也是可以进行判断的,关于数据结构和scanf函数的应用,ZJU那个算法写的很不错,以后会多借鉴一下那些技巧。自己在思考使用函数来存储,以及如何去接受来自文件的输入时,策略总是太差,原因也就是对那么函数不太熟悉。比如scanf是以空格和回车结束的,gets是以回车作为结束的。 C语言实现123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123#include<stdio.h>#include<string.h>char info[1000][1000];int M;char first[9] = {'9', '9', ':', '9', '9', ':', '9', '9'};char *fst = first;int fs = 0;char last[9] = {'0', '0', ':', '0', '0', ':', '0', '0'};int ls = 0;char *l = last;void input(){ scanf(\"%d\", &M); int i = 1; getchar(); while(i <= M) { gets(info[i]); i++; }}void calc(int f){ int i = 1; while(i <= M) { char (*p) = info[i]; while(*p) { if(*p == ' ') { fst = first; char *cin = p + 1; char *in = p + 1; while(*in) { if(f == 1) { //sign out time if(*in == ' ') { l = last; char *cout = in + 1; char *out = in + 1; while(*out) { //seek last person if(*l < *out) { //out copy to last memcpy(last, cout, 9); //remark last subscript ls = i; break; } else if(*l == *out) { l++; out++; } else break; } } } else { //seek first person if(*fst > *in) { //out copy to last memcpy(first, cin, 8); first[8] = '\\n'; //remark last subscript fs = i; } else if(*fst == *in) { fst++; } else break; if(*in == ' ') break; } in++; } break; } p++; } i++; }}void output(){ fst = info[fs]; while(*fst) { if(*fst == ' ') break; printf(\"%c\", *fst); fst++; } putchar(' '); l = info[ls]; while(*l) { if(*l == ' ') break; printf(\"%c\", *l); l++; }}int main(){ input(); calc(1); calc(0); output(); return 0;} 测试链接click me","tags":[{"name":"PAT","slug":"PAT","permalink":"http://turingxi.top/tags/PAT/"},{"name":"C","slug":"C","permalink":"http://turingxi.top/tags/C/"}]},{"title":"1005. Spell It Right","date":"2017-10-26T16:00:00.000Z","path":"2017/10/27/Spell/","text":"英文描述Reversible Primes (20) Given a non-negative integer N, your task is to compute the sum of all the digits of N, and output every digit of the sum in English.Input Specification:Each input file contains one test case. Each case occupies one line which contains an N (<= 10^100).Output Specification:For each test case, output in one line the digits of the sum in English words. There must be one space between two consecutive words, but no extra space at the end of a line.Sample Input:12345Sample Output:one five 中文描述正确拼写 给出一个非负的正数N,你的任务是去计算N的所有数字的和,并且用英语输出和的每一个数字。输入规格:每一个输入文件包含一个测试用例。每个用例占据一行包含一个非负整数输出规格:对于每一个测试用例,在一行内用英语字母输出这个和的数字。连续的字母用空格隔开,但是在行尾没有额外的空格。样例输入:12345样例输出:one five 分析与解题最大的和为3位数字,是一个99位的正数,且每位数字为9,也就是:99999……99999,一共99位,故最大的值为99 * 9 = 819。字符串作为输入,累积每个数字的和存放在整形变量sum中。之后求出这个三位数字每一位的数字,进行判断,之后输出,因为数字位数比较少,做4个判断即可正确输出。首先判断百位是否为0,若为零,则只输出十位和个位,否则就输出百位、十位和个位。同理,最后去判断十位即可。 C语言实现1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465#include<stdio.h>void output();char in[1000];char *p = in;int a1, a2, a3;void calc(){ int sum = 0; while(*p) { sum += (*p - 48); p++; } a1 = sum / 100; //百 a2 = (sum / 10) % 10; //十 a3 = sum % 10; //个 if(a1 == 0) { if(a2 == 0) output(a3); else { output(a2); printf(\" \"); output(a3); } } else { output(a1); printf(\" \"); output(a2); printf(\" \"); output(a3); }}void output(int a2){ if(a2 == 0) printf(\"zero\"); else if(a2 == 1) printf(\"one\"); else if(a2 == 2) printf(\"two\"); else if(a2 == 3) printf(\"three\"); else if(a2 == 4) printf(\"four\"); else if(a2 == 5) printf(\"five\"); else if(a2 == 6) printf(\"six\"); else if(a2 == 7) printf(\"seven\"); else if(a2 == 8) printf(\"eight\"); else printf(\"nine\");}int main(){ scanf(\"%s\", in); calc(); return 0;} 测试链接click me","tags":[{"name":"PAT","slug":"PAT","permalink":"http://turingxi.top/tags/PAT/"},{"name":"C","slug":"C","permalink":"http://turingxi.top/tags/C/"}]},{"title":"1004.Counting Leaves","date":"2017-10-19T16:00:00.000Z","path":"2017/10/20/Counting/","text":"英文描述Counting Leaves (30) A family hierarchy is usually presented by a pedigree tree. Your job is to count those family members who have no child.Input Specification:Each input file contains one test case. Each case starts with a line containing 0 < N < 100, the number of nodes in a tree, and M (< N), the number of non-leaf nodes. Then M lines follow, each in the format:ID K ID[1] ID[2] … ID[K]where ID is a two-digit number representing a given non-leaf node, K is the number of its children, followed by a sequence of two-digit ID’s of its children. For the sake of simplicity, let us fix the root ID to be 01.Output Specification:For each test case, you are supposed to count those family members who have no child for every seniority level starting from the root. The numbers must be printed in a line, separated by a space, and there must be no extra space at the end of each line.The sample case represents a tree with only 2 nodes, where 01 is the root and 02 is its only child. Hence on the root 01 level, there is 0 leaf node; and on the next level, there is 1 leaf node. Then we should output “0 1” in a line.Sample Input:2 101 1 02Sample Output:0 1 中文描述统计叶子 一个家庭等级体系通常被一个描述为一个家谱树被提出。你的工作是统计那些没有孩子的家庭成员。输入规格:每个输入文件包含一个测试用例。每个用例开始的一行包含N和M,N(0 < N","tags":[{"name":"PAT","slug":"PAT","permalink":"http://turingxi.top/tags/PAT/"},{"name":"C","slug":"C","permalink":"http://turingxi.top/tags/C/"}]},{"title":"1003.Emergency","date":"2017-10-18T11:11:03.000Z","path":"2017/10/18/Emergency/","text":"英文描述Emergency (25) As an emergency rescue team leader of a city, you are given a special map of your country. The map shows several scattered cities connected by some roads. Amount of rescue in each city and the length of each road between any pair of cities are marked on the map. When there is an emergency call to you from some other city, you job is to lead your men to the place as quickly as possible, and at the mean time, call up as many hands on the way as possible.Input Specification:Each input file contains one test case. For each test case, the first line contains 4 positive integers: N(<= 500) – the number of cities(and the cities are numbered from 0 to N – 1), M – the number of roads, C1 and C2 – the cities that you are currently in and that you must save, respectively. The next line contains N integers, where the i-th integer is the number of rescue teams in the i-th city. Then M lines follow, each describes a road with three integers c1, c2 and L, which are the pair of cities connected by a road and the length of that road, respectively. It is guaranteed that there exists at least one path from C1 to C2.Output Specification:For each test case, print in one line two numbers: the number of different shortest paths between C1 and C2, and the maximum amount of rescue teams you can possibly gather. All the numbers in a line must be separated by exactly one space, and there is no extra space allowed at the end of a line.Sample Input:5 6 0 21 2 1 5 30 1 10 2 20 3 11 2 12 4 13 4 1Sample Output:2 4 中文描述紧急情况 身为一个城市的紧急救援队的领导,你应该为你的国家提供一张特殊的地图。这张地图展示了被道路连接着的一些散落的城市。每座城市救援队的总数和每对城市之间道路的长度被标记在地图上。当其他城处于紧急状况呼叫你的时候,你的工作是领导你的救援队尽可能快的赶到处于紧急状况的城市,与此同时竟可能多的呼叫正在路上的救援队。输入规格:每个文件包含一个测试用例。对于每一个测试用例,第一行包含4个整数:N( <= 500)- 城市的数量(城市标号范围从0到N – 1),M – 道路的数量,C1和C2 – 你当前所在城市和你必须营救的城市。下一行包含N个整数,第i个整数代表第i城市中营救队的数量。此外的M行中,每一行描述由3个整数c1,c2和L组成的一条道路,c1和c2是一对被一条道路连接的城市,L是这条路的长度。此测试用例保证C1和C2之间至少存在一条道路。输出规格:对于每一个测试用例,在一行内输出2个数:C1和C2之间不同最短路径的数量和你可能召集的最大的营救队总数。在一行内的所有数字必须被一个额外的空格间隔开来并且在这个行的末尾没有额外的空格。样例输入:5 6 0 21 2 1 5 30 1 10 2 20 3 11 2 12 4 13 4 1样例输出:2 4 分析与解题问题:1. 求最短路径条数。2. 在多条最短路径中,求节点权值(营救队数量)之和的最大值。根据最短路径中上界性质:设G = (V , E)为一个带权重的有向图,其权重函数由w:E→R给出,其源节点为s,改图由算法INITIALIZE-SINGLE-SOURCE(G, s)执行初始化。那么对于所有的节点v∈V,v.d >= δ(s, v),并且该不变式在对图G的边进行任何次序的松弛过程中保存成立。而且,一旦v.d取得其下届δ(s, v)后,将不再发生变化。简要解释一下这个性质的含义。提出这个性质的问题描述是这样的:最短路径估计的值将会如何变化(最短估计路径也就是性质中的d值)。结论中的δ函数是指源节点到v节点的最短路径(注意此处是最短路径,并非最短路径估计值),也就是说最短估计路径的的值d(此后将用d来描述)将永远大于或者等于最短路径值。讲到这里需要提一下最短路径的引理:三角不等式,δ(s, v) <= δ(s, u) + w(u, v),这个例子我采用一种比较通俗的语言来解释,假设源节点s到目标节点d,存在一条最短路径p,p路径途径(u,v)这条边,也就是这条路径中是先到达u后到达v,那么也就是说,s到v的最短路径永远小于或者等于s到u的最短路径加上(u,v)这条路径本身的长度。举个生活中的例子,假如你从北京到广东,途径郑州和长沙,也就是说从北京到长沙的距离永远小于或者等于从北京到郑州的距离加上郑州到长沙的距离,也就是公式的由来δ(s, v) <= δ(s, u) + w(u, v)。接着,我们需要讲述另一个话题:松弛,所谓松弛也就是说根据情况来更新d的值,当一条边(u, v)中,如果v.d >= u.d + w(u, v)时就来更新v.d的值,令v.d = u.d + w(u, v)。通俗点讲是这样的,你从北京到郑州,有两条路,一条为北京-邯郸-郑州,另一条为北京-拉萨-郑州,当你算了一下北京到邯郸474km,北京-拉萨-郑州4064km,假如此刻v.d是4064km也就是北京-拉萨-郑州这条路,当你去查看u.d + w(u, v)时,也就是北京-邯郸为u.d,w(u, v)为邯郸到郑州的距离252,当4064 >= 474 + 252,也就是北京-拉萨-郑州这条路,要比北京-邯郸-郑州要长时,你就会去选择北京-邯郸-郑州这条路。言归正传,我们可以把上界性质中的d理解为三角不等式中的δ(s, u) + w(u, v),所以说,d将永远大于或者等于δ。此处有一个隐含了的条件。也就是v.d = δ(s, v)时候,v.d的值将不再变化。因此你可以利用这一点来求的多条最短路径,也就是说,当你发现v.d == u.d + w(u, v)时,意味着已经找到了两条相同的最短路径。 C语言实现123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188/* Emergency:shortest path* 图结构采用邻接表,最短路径算法采用dijkstra* init() 初始化操作,将除了源节点外的其他节点 d值赋值无穷,营救小队最大值初始化为0* relax() 如果v.d的值大于v.d + w(u, v)将v.d赋 值为v.d + w(u, v), 将v.maxamount = u.maxamount + v.rescue, 将v.shortsum = u.shortsum 如果v.d == v.d + w(u, v), v->shortsum = v->shortsum + u->shortsum, 如v->maxamount 小于u->maxamount + v->rescue的话,将 v->maxamount = u->maxamount + v->rescue*/#include<stdio.h>#include<stdlib.h>typedef struct arcnode{ int length; int info; //城市标号 struct arcnode *next;}arcnode;typedef struct vernode{ int d; int info; //城市标号 int shortsum; //最短路径条数 int maxamount; //不同最短路径营救小队最大值 int rescue; //每座城市营救小队数量 int visited; //最小优先队列使用 arcnode *next;}vernode;typedef struct{ vernode vertex[502]; int versum, arcsum;}AdjList;void init(AdjList *G, vernode *s){ int i = 1; while(i <= G->versum) { G->vertex[i].d = 65536; G->vertex[i].maxamount = 0; //营救小队最大值初始化 G->vertex[i].visited = 0; //访问标记 i++; } s->d = 0; s->shortsum = 1; s->maxamount = s->rescue;}void relax(vernode *u, vernode *v, int weight){ if(v->d > (u->d + weight)) { v->d = (u->d + weight); v->maxamount = v->rescue + u->maxamount; v->shortsum = u->shortsum; } else if(v->d == (u->d + weight)) { v->shortsum += u->shortsum; if(v->maxamount < (u->maxamount + v->rescue)) v->maxamount = u->maxamount + v->rescue; }}vernode *search(AdjList *G, arcnode *e){ int i = 1; while(i <= G->versum) { if(G->vertex[i].info == e->info) return &G->vertex[i]; i++; }}vernode *findmin(AdjList *G){ int i = 1; int min = 65536; while(i <= G->versum) { if(G->vertex[i].visited != 1) { //search for min if(min > G->vertex[i].d) min = G->vertex[i].d; } i++; } i = 1; while(i <= G->versum) { if(G->vertex[i].visited != 1) { if(min == G->vertex[i].d) { G->vertex[i].visited = 1; return &G->vertex[i]; } } i++; }}void dijkstra(AdjList *G, vernode *s){ init(G, s); int i = 1; while(i <= G->versum) { vernode *u = findmin(G); arcnode *e = u->next; while(e != NULL) { vernode *v = search(G, e); relax(u, v, e->length); e = e->next; } i++; }}void insert(vernode *L, arcnode *x){ x->next = L->next; L->next = x;}void input(AdjList *G, int *source, int *destination){ scanf(\"%d%d%d%d\", &G->versum, &G->arcsum, source, destination); int i = 1; while(i <= G->versum) { scanf(\"%d\", &G->vertex[i].rescue); G->vertex[i].info = i - 1; G->vertex[i].next = NULL; i++; } i = 1; int s, d, w; while(i <= G->arcsum) { scanf(\"%d%d%d\", &s, &d, &w); int j = 1; while(j <= G->versum) { if(s == G->vertex[j].info) { arcnode *p = (arcnode*)malloc(sizeof(arcnode)); p->info = d; p->length = w; insert(&G->vertex[j], p); } if(d == G->vertex[j].info) { arcnode *p = (arcnode*)malloc(sizeof(arcnode)); p->info = s; p->length = w; insert(&G->vertex[j], p); } j++; } i++; }}void output(AdjList *G, int destination){ printf(\"%d %d\\n\",G->vertex[destination].shortsum, G->vertex[destination].maxamount);}void emergency(){ AdjList a; int s, d; input(&a, &s, &d); if(s != d) { dijkstra(&a, &a.vertex[s + 1]); output(&a, d + 1); } else { printf(\"%d %d\\n\", 1, a.vertex[d + 1].rescue); }}int main(){ emergency(); return 0;} 测试链接click me","tags":[{"name":"PAT","slug":"PAT","permalink":"http://turingxi.top/tags/PAT/"},{"name":"C","slug":"C","permalink":"http://turingxi.top/tags/C/"}]},{"title":"1002.A + B for Polynomials","date":"2017-08-12T02:54:48.000Z","path":"2017/08/12/Polynomials/","text":"英文描述A + B for Polynomials(25) This time, you are supposed to find A + B where A and B are tow polynomials.Input Specification:Each input file contains one test case. Each case occupies lines, and each line contains the information of a polynomial: K N1 aN1 N2 aN2……NK aNK, where K is number of nonzero terms in the polynomial, Ni and aNi(i=1, 2, …, K) are the exponents and coefficients, respectively. It is given that 1 <= K <= 10, 0 <= NK <… N2 < N1 <=1000.Output Specification:For each test case you should output the sum of A and B in one line, with the same format as the input. Notice must be NO extra space at the end of each line.Please be accurate to 1 decimal palce.Sample Input:2 1 2.4 0 3.22 2 1.5 1 0.5Sample Output:3 2 1.5 1 2.9 0 3.2 中文描述多项式A + B 已知多项式A和B,求A+B输入规格:每一个输入文件包含一个测试用例。每一个用例占据了一些行,每一行包含了一个多项式的信息:K N1 aN1 N2 aN2…NK aNK,在多项式里K是一个非零项,Ni和aNi(i=1, 2, … ,K)是指数和系数。设1 <= K <= 10,0 <= NK < … < N2 < N1 <=1000。输出规格:对于每一个测试用例在一行内输出,输出格式同输入格式相同。输出的最后一个字符不能是空格。小数点精确都后1位。样例输入:73 1023 223 10-2样例输出:YesYesNo 分析与解题根据样例输入和输出初步得到,输入第一行为多项式A,输入第二行为多项式B,输出结果是合并同类项相关操作:指数相同系数相加减。隐含条件,如果系数为0则不用输出此项。例如:A = 3^2 + 0 ^3;B = 4^2,则结果A+B = 7^2。输入。根据条件Ni and aNi(i=1, 2, …, K),用一个整形数字来接受输入数据K。定义结构体数组,来接受输入数据Ni和aNi。计算。结构体数组A代替多项式A,结构体数组B代替多项式B。求A + B,如果A中指数同B中指数相同,则将A + B的系数存放在B中,否则,将A中指数和系数插入到B中。遍历数组A,查找A中每一个指数是否在B中出现,如果出现就更新B数组中系数的值,否则就将A数组中指数和系数插入到B数组。因此,定义三个辅助函数。Search():查找结构体数组A是否包含指数x;Update():根据指定下标更新系数值;Insert():将指定指数和系数插入到结构体数组中。考虑隐含条件,将B中系数不等于0的提取出,存放在C中。输出。将结构体数组C输出。 C语言实现123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125/* A + B for Polnomials * Input() 输入多项式* Output() 输出多项式* Search() 查找指数值,成功返回下标,失败返回-1* Update() 根据下标更新数组中系数* Insert() 向数组中插入指数和系数(有序表中插入元素使之依然有序)* Calc() 计算多项式A + B,并将结果存在多项式B中* DeleteForCore() 将Calc()计算结果多项式B中系数为0的数剔除,返回最终结果多项式C*/#include<stdio.h>typedef struct{ int exp[30]; double coe[30]; int size;}FA;void Input(FA *a){ int k = 0; scanf(\"%d\", &k); int i = 0; while(i < k) { scanf(\"%d%lf\", &a->exp[i], &a->coe[i]); i++; } a->size = k;}void Output(FA *a){ if(a->size != 0) { int i = 0; printf(\"%d\", a->size); while(i < a->size) { printf(\" %d %.1f\", a->exp[i], a->coe[i]); i++; } puts(\"\"); }}int Search(FA *a, int exp){ int i = 0; while(i < a->size) { if(a->exp[i] == exp) return i; i++; } return -1;}void Update(FA *a, int subscript, double coe){ a->coe[subscript] += coe;}void Insert(FA *a, int exp, double coe){ int i = a->size - 1; a->size += 1; while(i >= 0) { a->exp[i + 1] = a->exp[i]; a->coe[i + 1] = a->coe[i]; if(a->exp[i] > exp) { a->exp[i + 1] = exp; a->coe[i + 1] = coe; return ; } i--; } a->exp[0] = exp; a->coe[0] = coe;}FA DeleteForCoe(FA *a){ int i = 0; int size = a->size; FA c; c.size = 0; while(i < a->size) { if(a->coe[i] != 0) { c.exp[c.size] = a->exp[i]; c.coe[c.size] = a->coe[i]; c.size++; } i++; } return c;}void Calc(FA *a, FA *b){ int i = 0; int t = 0; while(i < a->size) { t = Search(b, a->exp[i]); if(t != -1) Update(b, t, a->coe[i]); else Insert(b, a->exp[i], a->coe[i]); i++; }}int main(){ FA a; FA b; Input(&a); Input(&b); Calc(&a, &b); FA c = DeleteForCoe(&b); Output(&c); return 0;} 测试链接click me","tags":[{"name":"PAT","slug":"PAT","permalink":"http://turingxi.top/tags/PAT/"},{"name":"C","slug":"C","permalink":"http://turingxi.top/tags/C/"}]},{"title":"1001.A + B Format","date":"2017-07-12T02:31:11.000Z","path":"2017/07/12/Format/","text":"英文描述1001.A + B Format (20) Calculate a + b and output the sum in standard format – that is, the digits must be separated into groups of three by commas (unless there are less than four digits).Input Specification:Each input file contains one test case. Each case contains a pair of integers a and b where -1 000 000 <= a, b <= 1 000 000. The numbers are separated by a space.Output Specification:For each test case, you should output the sum of a and b in one line. The sum must be written in the standard format.Sample Input:-1000000 9Sample Output:CYJJ 01-999,991 中文描述电话账单用标准的格式输出a + b和,标准格式是这样的:3个数字为一组,通过逗号间隔,如果测试用例少于四个数字就不用间隔。输入规则:每个输入文件包含一个测试用例。每个测试用例是一对整数,a和b,a、b的取值范围是-1000000到1000000.a、b之间用空格间隔。输出规则:对于每个测试用例,你应该在一行内输出a+b的和。和必须用标准格式输出。样例输入:-1000000 9样例输出:-999,991 分析与解题这是开始刷的第一个题,当时写了两个多星期,并且下面的代码是第三次重构,第一次写了400多行,我也是醉醉的。后来看了ZJU的代码只有几十行我就更是醉醉的了。我真不是一个聪明的人,要不也不会写这么多代码了。 C语言实现123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223#include<stdio.h>#include<string.h>#define MAXLEN 15 typedef struct{ char value[MAXLEN]; char pn; char len;}Number;Number a, b, sum;void Initialize();void DelForSubscrip(char *p, int subscrip);void InsForSubscrip(Number *p, int subscrip, char value);void DorpForSign(char *p);void ZeroFill(Number *p, int num);void Format(Number *p); //正序格式char Compare(char *p1, char *p2); //返回0,p1大,返回1,p2大void Operation();void Addition(Number *p1, Number *p);void Carry(char *p);void ReversalStr(char *p);void Subtraction(Number *p1, Number *p2);void Borrow(char *p, int i);int Output(Number *p);int main(){ Initialize(); Operation(); Output(&sum); return 0;}void Initialize(){ scanf(\"%s %s\",a.value, b.value); //符号位初始化 if(a.value[0] == '-') { a.pn = 0; //除去符号位 DelForSubscrip(a.value, 0); } else a.pn = 1; if(b.value[0] == '-') { b.pn = 0; //除去符号位 DelForSubscrip(b.value, 0); } else b.pn = 1; //长度初始化 a.len = strlen(a.value); b.len = strlen(b.value); //补零操作 if(a.len > b.len) //b 补 ZeroFill(&b, a.len - b.len); else //a补 ZeroFill(&a, b.len - a.len);}void ZeroFill(Number *p, int num){ int i = 0; for(; i < num; i++) InsForSubscrip(p, 0, '0');}void DelForSubscrip(char *p, int subscrip){ int i = subscrip; int l = strlen(p) - subscrip; for(; i < l; i++) p[i] = p[i + 1]; p[i] = '\\0';}void InsForSubscrip(Number *p, int subscrip, char value){ int i = p->len - 1; for(; i >= subscrip; i--) { p->value[i + 1] = p->value[i]; } p->value[i + 1] = value; p->len++;}void Format(Number *p){ int i = p->len - 1; int j = 1; for(;i >= 0; i--) { if((j % 3 == 0) && (i != 0)) InsForSubscrip(p, i, ','); j++; }}char Compare(char *p1,char *p2){ int i = 0; while(i < a.len) { if(p1[i] > p2[i]) return 0; else return 1; i++; }}void Addition(Number *p1, Number *p2){ int j = 0; int i = p1->len - 1; while(i >= 0) { sum.value[j] = (p1->value[i] - '0') + (p2->value[i] - '0'); j++; i--; } sum.len = p1->len; Carry(sum.value); sum.pn = p1->pn;}void Carry(char *p){ //*****初始化sum.value为0 int i = 0; while(i < sum.len) { if(p[i] >= 10) { p[i + 1] += 1; p[i] = (p[i] % 10) + '0'; } else p[i] += '0'; i++; } if(p[i] == 1) { p[i] += '0'; sum.len += 1; }}void ReversalStr(char *p){ char len = strlen(p); char e = len; len--; char i = 0; char tmp = 0; while(i < e / 2) { tmp = p[i]; p[i] = p[len]; p[len] = tmp; i++; len--; }}void Subtraction(Number *p1, Number *p2){ int i = p1->len - 1; int j = 0; while(i >= 0) { if(p1->value[i] >= p2->value[i]) { sum.value[j] = (p1->value[i] - '0') - (p2->value[i] - '0') + '0'; } else { p1->value[i - 1] -= 1; sum.value[j] = (p1->value[i] - '0') - (p2->value[i] - '0') + 10 + '0'; } i--; j++; } if(sum.value[j - 1] == '0') { sum.value[p1->len - 1] = '\\0'; sum.len = p1->len - 1; } else sum.len = p1->len; sum.pn = p1->pn;}void Operation(){ if(a.pn == b.pn) Addition(&a, &b); else if(Compare(a.value, b.value) == 0) //p1 - p2 Subtraction(&a, &b); else //p2 - p1 Subtraction(&b, &a); ReversalStr(sum.value); Format(&sum); //正序格式}int Output(Number *p){ int i = 0; while(i < p->len) { if(p->value[i] != '0') break; i++; } if(i == p->len) { putchar('0'); return 0; } if(p->pn == 0) { putchar('-'); puts(p->value); } else puts(p->value);} 测试链接click me","tags":[{"name":"PAT","slug":"PAT","permalink":"http://turingxi.top/tags/PAT/"},{"name":"C","slug":"C","permalink":"http://turingxi.top/tags/C/"}]},{"title":"HelloWorld","date":"2017-01-25T02:47:11.000Z","path":"2017/01/25/helloworld/","text":"前言在初中二年级的时候,我酷爱写作,身边有一位好友也喜欢写写东西。有一次我们两在聊天,谈到写作的事情,我向他提议说,我觉得咱两写的不能算文章,为什么这样说呢,你看我们读鲁迅,那才叫文章,如果我们写的也叫文章,那岂不是辱没了迅哥,所以我们以后如果说我们写文章,就说写字,比如,我问他,你最近写有字吗? 2012-2013荒诞而生猛的开始在2010年我进入高中,开启了高中生活,刚好在一年半的时候我所在的高中倒闭了,我的同学们在一周之内都离开了学校,同学口中说:这儿要倒闭了。当到了开学第一周的周五的时候,我的同学们走完了,最后我卷起了铺盖回到了家中。学校之前没有任何通知,期间也没有说明,我们每位同学也没有办理任何手续,班主任没有动静。之后我进入了一所拥有普通高中和职业中专两种办学性质的学校,当时我选择了理科,因为之前在倒闭的高中并没有分科,在这里安静的度过了半年的时间。在即将迎来我高三生活的前夕,一次由校长组织的动员大会中,内容是校长鼓励我们这些来自普通高中的同学们转入职业高中就读,之后讲了很多国家对于职业学校的扶持政策。那是我第一次和校长近距离接触,会后,我走到她跟前,激动的对校长说,我要转入职业中专,学习计算机专业。校长拍了拍我的肩膀说,好同学,我现在就给你安排。之后校长找到我现在班主任和计算机班的班主任,把我这件事办好了,并指着我,鼓励大家像我学习,都转入职业高中。事后,响应那次号召的同学们只有我一个人,源于对计算机的兴趣,也恰好开了这场动员大会。至此,在2012年的9月,我开始系统的学习计算机知识。 图片中坐在第一排中间有点胖的穿着黑色裙子的女人就是开动员大会的校长老师和课程在职业高中这里,学习了四门课程,vb程序设计,计算机网络,access数据库,组装与维修,这些都是职业高考将会考试的科目。其中组装与维修于我来学是比较容易学习的,原因是之前在这方面接触的较多。在初三毕业那年我因为个人爱好,就到电脑维修店里做过学徒,当时对硬件上的基础知识知道的比较多。程序设计是自己最感兴趣的,当时就认为学习计算机的就应该是写程序的样子,计算机网络和access数据库学的很迷糊。有幸的事情是遇见了我们的vb程序设计老师,他之前是h3c的硬件开发工程师,在浙江大学读的研究生。那时我的轴的比较明显的,尤其是在自己喜欢的事情上,我有很多想法和很多问题都会向vb老师请教,然而大部分的问题他都能给我做出解答。比如那个年代流行在QQ上偷菜,我就问他,我们能够写一个自动偷菜的程序,时间到了就第一时间去偷菜。他说,这个问题是可以实现的,但需要腾讯提供给我们相应的接口,要不你就只能通过其它手段去解决了。当时我也没有明白接口是什么含义,但隐约觉得是可以实现的。接着来聊聊vb老师,有一次我喝了点啤酒去上课,vb老师叫我出去,跟我聊了聊天,说你喝酒了就别来上课,可以请假,你如果过来上课就不要喝酒。后来我再也没有喝酒后去上课,至今如此。vb老师是一个很有个性的老师,讲道理浙江大学的研究生完全可以在一线或者二线城市找个工作买个房子,至今如此。他却选择留在我们那个县城教书,有一次我们聊到这件事情,他说是为了女儿而回到了家里。刚回来那会他在我们县城里一高教书,由于与领导发生了冲突,毅然辞去了工作,来到了我们这所高中。一高是我们县城最好的高中,而我所在的高中是我们县城最差的高中,至今如此。高考后去领档案的时候我们碰见了vb老师,我邀请他一起去吃个饭,他执意拒绝了,只是对我们讲,到了大学中要努力学习。关于其他老师,我记得access老师经常对着我们大吼,说讲的清清楚楚了,你们还不懂。网络老师一看我们不懂,他就比较着急,恨不得自己将粉笔写入黑板中。有一次我们看见网络老师在看机电方面的书籍,我们问他老师下学期要教机电了吗,他说是啊,你们要学习,我也要学习了。我们组装与维修老师会利用课余时间给我讲解关于IT中的奇闻异事,比如IBM和微软的故事等诸如此类的。 图中左边是vb老师我的学习我之前是一名学渣,来到职高后,变成了学霸,至今如此,这种变化是我预料不到的事情。刚刚步入职业高中那个班级的时候我做了两手准备,一手准备是我带了很多新的作业本是用来学习用的,第二手准备是我也拿了一些课外书,如果不行我就继续回到之前的生活,安安静静的做自己的学渣,课外书是学渣的标配。这里颇想说一番关于学渣的事情,直到现在我也不想去抱怨谁,我也不后悔。我在初中二年级就放弃了学习,在此之前我也算是名列前茅。初二后,我就不在去学习了,甚至说是厌学,这应该就是大家都要经历的,青春期的叛逆。当时我一度要决定踏入社会,我看到母亲的眼泪后,还是决定了继续读书。从初二到高二成绩一直都是倒数着,现在回想起来那四年的时光,真是迷人又梦幻。言归正传,其实我想说的是我为什么变成了学渣,以及学渣的程度。接着来聊我在职高的学习,开始上课后我就着迷了。这可以用一句爱情中经常说的话来描述,情不知因何而起,一往而情深。我想现在应该是找不到高中那时的笔记了,我记忆最深刻的事情是课堂笔记。尤其在vb课程中,我把老师说的每一个字都写了本子上。高三那年,老师是带着其他同学去复习知识,对于我来说是第一次学习。在2012年10月左右,学校为我们这些职高的同学开了一个补习班,专门补文化课,也就是语文数学和英语,是有偿补课的。那时候我竟然向我家长要钱说我要参加补习班,需要300块钱。父母一定是支持的,只是他们没想到我竟然真的开始用功读书了。其实有一次关于我那四年中为什么不读书有过一次问答,家人说了利害,我对他们说我都明白,但我就是这样,我也不知道为什么不努力读书。丢了四年的文化课,又何尝是一年半载就能补救过来的呢。语文自然不用说,大家都是中国人,成绩也都差不多,套用那时的一句话,好不到哪去也差不到哪去。英语和数学可谓一塌糊涂,主要问题是厌恶。毕竟四年中这两门课程是必学的,可能是我的厌恶劲儿还没过去,你去学习那些你不喜欢的东西,真的是及其困难的。这时候恰巧我有了一个好的环境,因为那些学习好的同学们总是在一起玩,所以我们一起报补习班的几个同学总是在一起学习。在他们的带领下,我也不得不去学习数学和英语了,这可能就是大家现在常说的氛围。我记得我和王恒喜欢互相攀比学习,比如我5点起床,他就4点50起床,我下课学习,他下课也学习。当然也有互相帮助的时候,比如我组装与维修学的好,我就把相应的要塞说给他听,他数学比我好,就常常给我讲解数学题,虽然我依旧听不懂。转眼间那一年就在这样的紧张又开心的日子中度过了。现在总结起来,我倒是觉得我第一学期比第二学期要努力很多,这是我自己的感受,可是第二学期基本都是每天5点起床。高考结束后,我最想做的事情就是睡觉了,真的是最想做的事情。现在回想起来,的确是不健康的作息。每天9点半回去,5点起床,每天也就睡6到7个小时,整天处于一个鸡血状态,跑着去吃饭,跑着去学习。不过最后的成绩也是不错的,虽然我是第一年学习职高的课程,但是取得了班级前5的名次,至此我成了学霸了。等到我去回首那一年的时候我最大的收获并不是我学了多少计算机知识,而是vb老师给了我方向,让我知道了我想要的是什么。这样的努力也让我变得比之前自信了,相信只要付出就会有收获。 图中右边是我的学习战友王恒我的感想那一年的时光是无法描述的,就算时隔这么多年,我也不知道如何去评判那一年。如果没有那次荒诞的动员大会,如果我没有选择去学习计算机,如果我没有努力学习,如果我父母不支持我去职业中专,如果我高考准考证没有办下来……有时候我也在想所谓天时地利与人和可能就是那样吧。总之那年一是我这几年后的一个转折点,也可以说是我未来人生的一个转折点。 早上5点半的职教中心 我的文艺课桌 2013-2016全国计算机等级考试我有个朋友是我同乡,他早我一年来到了新乡学院,快要开学那几天我,我询问了很多关于大学生活的事情,他说你还蛮期待,我说是有些激动。开学那天,父亲同我一起来到了学校,第一次还走错了,导航到了一个和新乡学院名字一样的学校,下车后发现这所学校同我的高中大小一样,心里犯嘀咕大学不该是很大吗,后来再次搜索后,抵达到了目的地。我的同乡接待了我,这让我省了很多事情,我想这对于他来说可能是举手之劳,可是对于我来说却给予了我很大的帮助。我那时心里想的只有一件事情,那就是学习。我记得最清楚的时候是我每天早上6点半起床,吃饭后就去背英语,我站在学校的小湖边,那篇欢迎我们来到大学的英语文章我背的及其熟练,那也是我背诵的第一篇文章。不过这样的日子好像是持续了半年,就不在起床背英语了。不过每天起床跑步的习惯还是在坚持,跑步我是跑了一年半,每天都在跑。在职高的时候听魏老师讲上大学要多考几个证,比如说是计算机等级证。我期初觉得那应该是相当困难的事情,所以对待这件事的态度就变得极其积极。期间还发生了一件让我转变学习态度的事情,也就是在大一的上半学期,我竟然挂科了一门考查课,按照学长的嘱托,说考查课是绝对不会挂科的。我们的考查课是可以翻书的,可是就算那样我竟然还是挂科了。这让我对学习这件事情发生了新的思考,我知道我为什么挂科,主要的动因是那门课程我觉得没意思,所以就不想听课,比如高等数学,我就不想听课。那时竟然天真的认为大学就没有了数学和英语,大学不仅有数学和英语,而且他们的地位同高中时候一样重要。我那时也还在想,我是来学习计算机的,为什么还要开思修了、大学生心理学这些课程,这不就是在浪费时间。那时我对于没见事情都有自己的看法,思维是多么的敏感。那次挂科后,我翻来覆去的在想,为什么还是会挂科,我不在去想我不学习高数和模电的原因了,而是告诉自己不能在挂科了。等到第二学期的时候,我硬着头皮去学了高数,竟然得了八十多分,那一刻我觉得我的任督二脉被打通了。接着说登记证,我报的第一个等级考试是C语言的,因为等级考试都在刚开学那一会,比如春天开学3月份那会和夏天开学9月份那会,所以一到学校我就开始忙了,基本上大一大二每年的开学我都在准备等级考试。那些日子,我就每天背着我的电脑在自习室刷着等级考试的题目,我记得C的特点是选择题比较难,后面的编程填空题和编程题都比较简单,我记得考试时候写选择题用了50多分钟,而后面的编程题10分钟就写完了。考完二级就去考了三级,再考三级的时候也整明白了等级考试的具体制度,二级和三级是有联系的。比如你二级考了C,你三级可以考网络,但是不能考数据库。我三级考的是网络,我是和二级一起考的,第一次的时候三级没有过,第二次就过了。他两的成绩都在八十多分,第二次考网络那会也是蛮紧张的,第一次没过,可以解释说和C一起考,没准备好,可是这次是单独考了网络。考网络那会身边也没有其他同学懂,所以遇到问题很多事情只能自己一个人解决,那会我学会了解决问题的一个特别的方法,我通常加一些相关的群,遇到不会的就会在群里问,如果没人回答,我就私聊那些不认识的人,这样总会得到一些答案,有时候也会是正确的答案。在大学里我的网络老师很厉害,我的意思他讲课很厉害,总能把浅显的道理说的明白,这点是挺令我敬佩的。不过在我考网络那会,他刚好被学校派出去工作了,所以也没能问上他。在考证期间,有同学劝我,考这个没什么用,我不知道如何回答,只是记得常会说,嗯,我知道,不过考这个也没有什么坏处嘛。至此我来到大学的目标算是完成了,没拿下之前会觉得很难,真正做到了,感觉并没有想象中的那么困难。可能这个过程中,我没有去想太多其他事情,一心就想着考试了。 兰州和机器鱼接着要聊得这段时光,是令我怀念的。在新乡学院三年,每年的暑假我都会留在学校,第一年是准备蓝桥杯,第二年就是机器鱼了。15年的夏天,我睡过宿舍六楼的宿舍,我睡过实验室的地板,我也睡过宿舍的天台。每次留宿都需要从原来的宿舍搬出来,到指定的暑假留宿宿舍去,这次竟然被分配到六楼了,一个房间4个人,中午的酷暑让我们觉得自己像烤面包机里的面包一样,至今我都能记得那暴热的味道,因为太热了,我们晚上就跑去天台睡觉,早上5点就又会被太阳晒醒了。当时我们那么多人在一起,也不觉得条件有多艰苦,也不去抱怨什么,只是每日在暴热中度过,渴望每天都会下雨。在这个阶段的时候,我其实是在考虑要不要再去考计算机四级,而我觉得网络也就那样,没什么意思,当时我的心就跑进了嵌入式那边,觉得是嵌入式是一个挑战。接着就来聊聊机器鱼,机器鱼就是一个嵌入式设备。机器鱼本身的系统是ucos,我们通过一个客户端软件,编写代码,之后再写入机器鱼中。我们的竞赛要求是沿着白色pv管道行走,同时遇到黑色点的时候发出警告。我们有四五个函数可以用来控制机器鱼,最主要的是舵机角度和舵机力度。我们有6个传感器,通过判断这6个传感器的值来决定行走的路线,第一步,沿着白色pv管走的目标很快就实现了,只是检测黑色点的情况一直没有达到最好。那些天,我们还有老师基本每天都在实验室中度过。傍晚的时候,老师会请我们吃饭,我是那个去买饭的人,提着一堆饭回来,大家边吃边聊技术,感觉是一件很美好的事情。在七月底八月初的时候,我们一起前行到了兰州来。在兰州的那一周和之前的生活来比简直就是天堂地狱了,我们住如家,吃兰州拉面,坐汽艇游黄河。GLM和SQL北京和工作不要温柔的走进那个良夜 2016-2018生活并不总是一帆风顺begin again","tags":[{"name":"生活","slug":"生活","permalink":"http://turingxi.top/tags/生活/"}]},{"title":"图书推荐","date":"2015-12-27T06:29:52.000Z","path":"2015/12/27/machinelearning/","text":"前言在今年夏天的时候,一个老师找到我,给我介绍了他正在做的毕业设计,他想让我帮他把算法写出来。毕业设计的课题是关于大数据的,他建议我看看这些书籍:python的、机器学习的、hadoop的。前两者我都看了,没有去看hadoop。python看的是《笨方法学习python》,机器学习看的是哪本书也记不起来了,只是看了开头的分类算法。今年三月份的时候在中国mooc上看了关于python的数据分析课程,当时是为了帮助我的一个朋友写毕业设计,当然了这个朋友是女生。其实关于这方面自己知道的仅仅是皮毛而已,无论是帮助女生写毕业设计的算法还是帮助老师写毕业设计的算法,都是在百度上查阅些资料,看一看别人的代码,后来发现百度搜索到的相关内容质量不太高,就又去了知网上看看相关的论文和期刊。 数据老师从学校的图书馆系统中把相应借书数据搞了过来,本来说是要搞建立电子图书系统以来的所有数据,后来我听他说导出数据的过程中,服务器就宕机了,他也就作罢了。刚开始的时候给我的数据是下面这样的,我问他那个是主键,他说可能是mrac记录号、或者是读者证件号。这些数据没有图书表也没有学生表,不过是可以去做分析的,因为数据有就可以了,只不过没有办法去验证结果,也没有办法做成能显示的的结果,原因是你根本不知道数字背后代表的意义。后来就又给我发了这样的数据,有了读者名字和图书名字的借阅表。有了这样的数据后,我要做三件事情,首先把所有读者整理出来,接着把所有图书信息整理出来,最后把借阅表整理出来。也就是我需要把这一张表变成三张表。上面这张表中有学生姓名和学生id,通常来说,一个学生对应一个id但也不外乎有重名的情况,但id总归是主键,是不会重复的。当我以为id是唯一的时候和以为学生姓名是唯一的时候,就得到了下面两个图片。 当时我看到这样的情况就有些烦恼,我想这些数据不一定准确,是需要进行下一步清洗工作的。现在我接着检索了这些数据发现了这样的情况,一个学生叫付会敏,他有两个id,但是这两个id中的其中一个id又有两个名字。也就是说付会敏对应id为820000000和820016922,这其中820000000对应两个名字付会敏和赵文杰。其实最简单的一个处理方法就是将这些脏数据全部清除掉。也就是说,将重复的学生id和重复的学生姓名,从数据库中删除,之后用相同的办法去处理图书信息。当然如果所有的数据都是这样的话,那就不能用删除这种操作才处理了,因为如果删除了,数据库里就没有了数据,没了数据那么后面的事情也都不用做了,不过这次还是蛮好的,我看了数据总量和脏数据量,比例是20比1,也就是脏数据还是少量的数据。我不记得我是如何做的了,我记得当时是分别整理出了三个excel文件,分别是学生信息和图书信息以及借阅信息。由于原文件数据过多,我在单机调试行缩减了很多数据,这样才使得单机程序跑的出结果来。关于上面使用的查阅,在一个借阅信息表中,查询出一个name下的多个id。首先是找到那些一个名字有多个id的学生,对应下面代码的tmp表,之后在所有学生表中使用in关键字去显示这些有多个id的学生。关于sql的查询的书写规范我一直不太清楚,因为在sql中的确很少会有超过100行的查询代码。1234567891011121314151617181920select *from ( select st_name, st_id from libinfor group by st_name, st_id ) t1where t1.st_name in ( select tmp.st_name from ( select st_name, st_id from libinfor group by st_name, st_id order by st_name ) tmp group by tmp.st_name having count(1) > 1 ); 协同过滤我在看机器学习的时候,老师叮嘱我可以先去看看协同过滤。我就在机器学习那本书中找关于协同过滤的章节,还是没有发现。后来我在百度找到一篇关于电影推荐的协同过滤算法, 跟着技术博客敲了敲代码,可是最后我去测试的时候发现推荐的八竿子都打不着。后来又大量阅读了资料,由百度到论文期刊以及一本关于推荐系统的书籍。最后在InfoQ上找到一篇讲解协同过滤的,感觉说的最为清晰明白,后来发现是翻译自Building Recommenders的。这里我就再把之前看的这篇文章再看一遍,来总结一下什么是协同过滤。首先我先来回忆一下,我记得最典型的就是向量和分类,因为在数学之美上也写过这样的文章,当时讲解了斜率和分类的关系,其原理就是托尔斯泰那句名言:幸福的家庭都是相似的,不幸的家庭各有各的不幸。我当时写的协同过滤是基于item的,这里要说一下,协同过滤一般分为基于item和基于user的,到底什么时候用哪个,要根据数据的特点来决定,当然复杂的时候也是可以混合使用的。首先是得出这本书被谁借阅过,假如有100个同学,用0来表示没有被借过,用1来表示被借过。那么这就是一个有100项的向量。","tags":[{"name":"Python","slug":"Python","permalink":"http://turingxi.top/tags/Python/"},{"name":"SQL","slug":"SQL","permalink":"http://turingxi.top/tags/SQL/"},{"name":"机器学习","slug":"机器学习","permalink":"http://turingxi.top/tags/机器学习/"}]}]