第二篇:浅谈算法设计思路
大家好!
在上一篇中,我不厌其烦地又把校园宣讲时说过好次的内容敲了一遍,只是希望对那些没能参加的同学能有一点点的帮助
这一篇,就跟大家分享一下我对这个题目算法设计的一点想法。
首先,有一点要声明:以下内容只是我的一些想法,可能有不妥当之处,也难免有疏漏之处,大家在实际设计和编写算法的时候,还是要自己多想多尝试,不能简单地把以下内容代码化:Z 。
其次,欢迎大家回帖进行讨论,都说说自己的看法。这次竞赛本来就是一个难得的交流的机会,我相信学到知识,得到成长才是最重要的。
最后(正文开始之前的最后……),我个人不对下面的内容保留任何著作权,也希望大家遵循这样的约定,拣点可以让别人拿去用的东西交流
时间篇
对于算法的时间限制,包含两条,即单步时间和总时间,具体的内容楼上有介绍,这里不再赘述。
如何保证算法的耗时不至超出规定的时间限制呢?
在题目文档的最后,示例代码中演示了一个计时框架。在那段代码中,使用了Stopwatch进行方法内的计时,考虑了单步时间和总耗时,并且给出了一下buffer的空间,防止因为方法调用、返回、以及其它不可预期的情况造成意外超时。这个框架演示的方法应该还是值得借鉴的,但是其中的一些硬编码的数值可以根据个人的需要或者实际测试数据来确定。
如果算法的一次调用要执行大量操作,那么应该将这些任务合理地划分成子任务,并保证子任务消耗的时间不至于过长。同时,在子任务衔接的时间点,适时地检查剩余时间,防止超时。
如何合理地规划利用时间呢?
一局比赛的总可用时间是固定的,每一回合的可用时间也是固定的,但不同的数据需要执行的回合数和需要处理的数据量却是不确定的!
所以,在一局比赛中的某个时刻(通常是开始时),大致地估算比赛的进程是一个有意义的尝试。通过分析数据,大致估算一局比赛要进行多少回合,这样对于合理利用时间,使每一次被调用时的用时更高效是很有帮助的。
另外,通过分析已经执行的操作和当前地图信息,实时地更新时间片使用规划,能够更好地利用剩余的时间。
如何应对大规模的数据呢?
很多同学都问到建筑列表的规模问题,我的回答是没有确定上限。
这样的设置,是对选手算法数据处理能力的一个考验。建筑的数量可能会很大,如何有效地处理大量数据,既保证不超出规定的时间,又得到较高的分数是算法优化的一个重要方向。
简单地说,保留中间结果,减少重复操作、分批处理,避免单次处理过多数据、适当取舍,退而求其次,这些都是可以考虑的方法。
空间篇
算法的空间限制,同样有两条,详见上文。
如何控制和计算算法的空间使用呢?
竞赛平台并没有提供算法内存使用情况的相关信息。所以,在内存控制这方面,选手需要靠自己的努力了
其实,通常情况下,算法使用的内存,只需要被控制在一个比较粗略的范围内,比方说200MB。这样,查看Windows任务管理器中的内存使用量变化就可以大概估算了。另外,可以自己计算一下算法中消耗内存较多的数据结构的体积。
有了观测数据和计算数据,就可以在算法中添加一些条件,来限制内存的使用了
如何合理使用内存呢?
首先,应该合理地选择数据结构,尽量高效地利用空间。
其次,应该合理地规划内存使用,把好钢用在刀刃上。
除了以上两点宽泛的原则,还有一个使用内存中的小技巧,或者说是一个注意事项。与C++的显示内存管理不同的是,CLR提供了一套完善的内存管理机制,程序中并不需要显示地进行内存的申请和释放,无效对象占用的内存会在适当的时机被自动回收。我们的竞赛平台中,在每一次调用选手算法的前后,会先调用CLR内存回收机制清理内存垃圾,再进行内存用量的计算。
为了使已经失效的对象占用的内存能成功地被释放,需要消除对其的引用。简单地说就是,如果你创建了一个对象,完成了任务之后想让它被回收掉,只要将所以指向这个对象的引用指向其它位置即可。参见以下两段代码示例:
object obj = new Object();
obj = null; // 此时,上一行创建的对象已经可以被回收
object obj = new Object();
obj = new Object(); // 此时,上一行创建的对象也可以被回收
策略篇
获得分数奖励,有三种方式。这三种得分方式楼上有介绍,下面主要分析如何设计算法,获得尽可能高的分数。
摆放面积越大的建筑越好么?
通常情况下,摆放面积越大的建筑能够立即得到的分数越高。但立即得分只是总分中的一部分。
建筑一定要放在同功能的地图区域上么?
建筑覆盖同功能的地图区域,可以获得额外的分数奖励。一般来说,有奖励的分数拿,何乐而不为呢
怎样获得比对手更高的分数,赢得比赛呢?
放面积尽可能大的建筑,覆盖尽可能多的同功能区域,这些基本的策略很容易被实现。所以,如果只做以上考虑,你的算法很难脱颖而出。一些额外的策略优化对于提升算法的分数是必须的。
一局比赛结束时,会根据算法覆盖的各类型区域的面积,进行一定的分数奖励。想方设法获得尽可能多的分数奖励,是提高算法得分,拉开与对手分数差距的一个有效途径。
对弈的游戏,拼的就是一个此消彼长,你死我活。所以,运用一些策略,使得在自己得分不受或少受影响的前提下,对手的得分受到较大影响,也是战胜对手的一个有效途径哦:smoke
技术篇
相信很多同学都是第一次接触.Net,下面介绍的一些.Net内置类型,可能会对算法的实现有帮助。
Array,即数组,如int[]。固定容量的线性存储结构。对其成员操作的时间复杂度是常数级。
List<T>,泛型的列表。可变容量的线性存储结构。与C++中的List不同的是,.Net中的List采用线性存储,对成员的访问是常数时间复杂度,并提供线性时间复杂度的插入和删除操作。其空间复杂度是O(N)。
Queue<T>,队列。.Net的队列是无优先级的简单队列,采用线性存储,提供线性空间复杂度及常数时间复杂度的入队和出队操作。
Dictionary<TKey, TValue>,字典,一种带索引的散列存储结构。这种结构的实现比较复杂,可以简单地认为它提供了亚线性时间复杂度的添加,查找操作,以及亚线性的空间复杂度。这是一个比较好的实现大量数据存储和查找的方案,但要注意key的选取,这个对性能的影响很显著。
好吧,今天暂时想到这些,先写到这里,大家如果有什么问题,欢迎回复提问。如果发现以上内容有什么错误或疏漏,也欢迎回帖指正。 |