Archive for May, 2007

“彩球游戏”题解
Saturday, May 26th, 2007

郑重声明:本人并不自认为“会”此题,只是AC了,说说自己程序的思路而已。
原题:http://hi.baidu.com/astar/blog/item/fe22ab18c3c54b0635fa4192.htm
首先要对输入的数据进行预处理,按照“有连续的X个颜色为Y的小球”的格式,将连续的同色小球一起考虑。s[i]表示第i组小球的数量,type[i]表示第i组小球的类型。注意,所有连续的大于等于M个小球,都可以等价地当作M-1个小球来处理。
设计状态为v[i,j,k1,k2],表示第i组小球到第j组小球的左边多了k1个与s[i]同色的小球右边多了k2个与s[j]同色的小球时被完全消去的最小代价。平凡的状态包括:i>j时,状态的值为0;s[i]+k1>=M时,状态的值为v[i+1,j,0,k2];s[j]+k2>=M时,状态的值为v[i,j-1,k1,0];i==j时,状态的值为M-(s[i]+k1)。
对于非平凡的状态,只需要考虑最左边的一组小球或最右边的一组小球如何处理。以左边的这组小球为例,可取的策略有两种:使用M-(s[i]+k1)个小球将它们单独消去;设s[i]与s[x]同色,在[i+1..x-1]这个序列被先行消除后,将这组小球与s[x]合并后再消除。右边的小球也可以同样定义这两种策略。还有一种策略就是,当s[i]与s[j]同色时,将[i+1..j-1]这个序列先行消除,再将s[i]、s[j]以及k1+k2个小球同时消除。状态转移方程如下:
v[i,j,k1,k2]=min{
M-(s[i]+k2) + v[i+1,j,0,k2];
M-(s[j]+k2)+v[i,j-1,k1,0];
v[i+1,x,0,0]+v[x,j,s[i]+k1,k2] (if type[x]=type[i]);
v[i,x,k1,s[j]+k2]+v[x+1,j-1,0,0] (if type[x]=type[j]);
v[i+1,j-1,0,0] (if type[i]=type[j]&&s[i]+s[j]+k1+k2>=M);
v[i+1,j-1,0,0]+(M-(s[i]+s[j]+k1+k2)) (if type[i]=type[j]&&s[i]+s[j]+k1+k2<M);
}(i<x<j)
很麻烦吧……我也觉得很麻烦。 如果谁能简化一下那真是太好了。
这样的话,一共有O(N^2*M^2)个 状态,每组状态的转移需要O(N)的时间,总复杂度是O(N^3*M^2),不就严重TLE了么……话是这么说,也许这道题就能告诉我们Big O这种关于复杂度上界的分析有时是会不靠谱的。
具体实现时一定要采用自顶向下的DP实现,也就是所谓的记忆化搜索了。这时你会发现,实际需要计算出来的状态只占总状态数的一小部分。我对这题的4320个数据做了简单的统计,需要计算的非平凡状态的数量平均为2057.766个,其中此数量为0~1000的有2244个,1001~5000的有1541个,5001~10000的有393个, 10001~20000的有139个,20000以上的只有3个,最多的需要计算26621个状态。
我想这个题应该可以得到一个比O(N^2*M^2)更好的需要计算的非平凡状态数的上界,可本人算法分析的能力有限,希望有高人能够在这一点上指教一二。
最后说一下空间问题,如果开一个200×200×20×20的四维数组的话似乎太浪费空间了,另外,由于“内存分页”等我也不太理解的原因,程序的空间用量对实际运行时间(并非“用户时间”或CPU时间)是有影响的。我采用的方法是每次动态分配一个N*N*M*M的一维数组,用来模拟四维数组。例如状态v[i,j,k1,k2]在这个数组中的下标就应该是((i*N+j)*M+k1)*M+k2。当然,也可以将它理解成一种没有冲突的hash——将取值有限制的四元组映射为一个整数。对于本题的数据来说,我的程序最多时用了4128KB的空间。
于是,这道题的4320个数据就被比较完美的解决了。注意我并不是说这道题被完美的解决了,因为算法的理论复杂度仍然很高,不能保证无法设计出达到理论复杂度上界的数据。
欢迎批评指正。
zuma.rar 这是我做的cena评测包,内有我的程序,数据由陈启峰提供,共10组输入输出文件,每个输入文件中含432组数据。

Tags: 彩球游戏, 百度之星

Related posts

百度之星复赛
AStar2007进复赛了

近期要实现的数据结构与算法
Thursday, May 24th, 2007

从未实现过的:
Dinic算法(网络流):NOI Profit等。
Stoer-Wagner算法(最小割):UVA 10989。
Trie图(字符串自动机):SPOJ WPUZZLES、POI #7 Virus、Ural 1158、Ural 1269。
块状链表(链式数据结构):NOI的题。
以前实现过,但是需要活用的:
线段树的题目:PKU 3225。
平衡树的题目:NOI的题。
6月份的前两周把它们做完。

Tags: 计划

Related posts

2007六月计划
2007年要做的5件事
2007寒假计划
计划7.11-7.20

算法资料网站列表(收集中)
Tuesday, May 22nd, 2007

本列表收集算法资料丰富的网站,目前仅收英文网站。排名不分先后。
http://www.nist.gov/dads/ Dictionary of Algorithms and Data Structures,基本上各种数据结构和算法都会有介绍,个别的介绍太简短,没提供什么有用信息,大多数词条还是蛮有价值的,特别是有些More Information里面的链接。
http://www.research.att.com/~njas/sequences/ 在线数列百科全书。我只能说……谁用谁知道。
http://compgeom.cs.uiuc.edu/~jeffe/ Jeff Erickson教授的个人主页。这个教授真是一个好人,把自己的课程的一切资料——讲义、试题——放了上去。比如说那份详实的Algorithms Course Materials就是一份不可多得的好资料。
http://www.csse.monash.edu.au/~lloyd/ Lloyd Alison教授的个人主页。我发现里面关于某些东西(例如Suffix Tree)的介绍很不错,还有Algorithms->2D->Life里面的东西你一定要看看。
http://www.introductiontoalgorithms.com/ 一个关于Introduction to Algorithms这本书的网站,上面有用的资料不比原书中多。不过若你没有这本书,只是想对于某个算法找到其定义或者伪代码,这个网站还是有用的。
http://www.cs.fiu.edu/~weiss/ Mark Allen Weiss教授——Data Structures and Algorithm Analysis in C/C++/Java 等书的作者——的个人主页。这里有这些书的源代码,我认为这些代码是很好的。
http://www.cs.kent.edu/~rmuhamma/ Rashid Bin Muhammad教授的主页,里面的Algorithms页面挺有料。
http://courses.csail.mit.edu/ 一份MIT的课程列表,其中的6.851很神奇。
http://www.math.ucla.edu/~tom/ Thomas S. Ferguson教授的个人主页,我在上面下载了一份不错的Game Theory讲义。
哦……这个列表目前很短,因为我很菜,知道的东西还很少。如果你知道包含好的算法资料的网站,请一定要留言告诉我哦!

Tags: 算法, 网站

Related posts

Hungary 算法
一些好网站
求最大权二分匹配的KM算法
并查集
过度热情计算

一些好网站
Monday, May 21st, 2007

煎蛋:http://jandan.net/,当我打算看点儿东西用来放松时的首选方式。
草莓周刊:http://memedia.cn/,反正我觉得真是很好看。
那吒Anothr:http://anothr.com/,我目前最喜欢的RSS解决方案。(我的页面:http://anothr.com/tianyi。)
还有一些“非法”甚至“反动”的,就不说了。

Tags: 网站

Related posts

算法资料网站列表(收集中)

《Code Complete》笔记(六)
Monday, May 21st, 2007

第23章
调试本身并不是改进代码质量的方法,可以认为它只是一种补救措施、不得已的手段。在OI中应该尽量避免调试,应该在一开始避免错误的发生。调试的效率在人与人之间有巨大差距,所以培养调试能力十分有必要。
寻找缺陷和理解缺陷是调试中最费时的步骤。事实上在这点上不应该吝惜时间,一定要确保已经完全理解缺陷后再开始动手修改。
把需要尝试的事情在纸上逐条列出,这也许是一个加速调试得好途径,因为调试时的人大多都昏头昏脑的,最好有一个能让你保持清醒的东西。
优先检查最近修改过的代码。尽量对程序保持全局理解。永远不要随机地、尝试地修改代码,每一处代码改动都需要有明确的理由。一次只做一个改动。
心理因素让人看到他“希望”看到的东西,这对调试的影响有点大,所以要保持优秀且一致的编程习惯,要让变量名之间的“心理距离”尽量远。
把编译器警告级别设为最高,这是默认的。Profiler也可以当作调试工具,有时从不正常的运行时间或调用次数中可以看出错误。
第24章
一帆风顺的软件项目是神话,实际中的代码是经常需要经受剧烈变化的。好在实际中引起变化的(最?)重要原因——需求的变化——在我们的OI中不会出现,只要你别读错题了。
软件演化的基本准则是,演化应当提升程序的内在质量。
重构的定义是“在不改变软件外部行为的前提下,对其内部结构进行改变,使之更容易理解并便于修改”。需要重构的理由在OI中同样充分的有:代码重复,冗长的子程序,嵌套过深,过长的参数列表,命名不当。
特定的重构方法很多,也很系统,但在OI中还是应该尽量在一开始就写出更好的代码,毕竟你没有太多时间。
如果确定要对代码——特别是正确(指不可能WA)的代码——进行某种大幅度的修改(不一定是重构),一定先备份一下。
第25章
性能调整在OI中也许没有你一开始想象得那么重要,似乎复杂度才更王道。任何情况下“性能”都不是头等大事,正确性才是。
程序需求方面,就是可能不切实际的复杂度分析,少数时候实际运行时间无法用理论复杂度衡量。程序的设计就是基本的算法了,如果这里面出了致命问题,再多的代码调整也无济于事。最后的手段才是代码调整。
代码调整的问题在于,高效的代码并不一定就是“更好”的代码。80/20法则是一定要注意的,Profile一下嘛……“随时随地”的优化是绝对不可取的。
常见操作的相对效率那个表真是好东西。它说我们整数的赋值、整数的加减乘、浮点数的加减乘、调用一个函数、用下标访问数组等操作所需的时间都相差无几!这还真是惊人的结论……(有空还是自己验证一下。)
优化前,一定要保存代码的可运行版本。
第26章
OI中可以考虑的代码调整技术:用查询表替代复杂表达式(没用过呢还)、使用惰性求值(常用的思想);展开(这个很有点意思)、哨兵值(这个我比较喜欢)、把最忙的循环放在最里层(可能被忽视的常识)、削减强度(值得注意);尽量减少数组引用、使用缓存机制(事实上很高级的东西,值得简单地尝试);利用代数恒等式(如果真的有而你没发现的话……)、削弱运算强度(常用的)、使用正确的常量类型(以前没有注意过,也许真的很重要)、删除公共子表达式(为了程序清晰也应该做);把子程序写成内联(“现代”计算机说这样做不一定就会性能提升)。

Tags: Code Complete

Related posts

《Code Complete》笔记(二)
《Code Complete》笔记(一)
《Code Complete》笔记(三)
《Code Complete》笔记(五)
《Code Complete》笔记(五)