华为软件精英挑战赛-2020-初赛复赛-题目分析/算法Baseline (求出有向图中所有的环)
2020.06.01更新:
开源比赛代码
题意:给定一个有向图,求出图中所有长度在[3,7]之间的环。
输入:格式为[IDU,IDV,Weight]的边表,ID为32位无符号整数(当然题目说明了,小于2^31,所以int32就好),边最多28W条,不重复,结点平均度数小于10。环中同一个ID不可以重复出现(若大环包括小环,则需要分开统计),环的个数不大于300W。
输出:环的个数,按照1.环长度;2.环的数字字典序输出所有环。
举个例子:1,2,3的全连通图有两个环
初步思路:
- 要避免搜索出的路径重复,需要制定一个环的标准型,以题目为例,就是最小的ID在环表示的首位
- 题目要求最小的结点在每个路径的第一位,故对ID排序后,重映射所有结点序号为[0,n),在搜索时,如果直接对某个结点的出边从小到大搜索,最后得到的答案自然是排好序的(虽然排序一遍答案花掉的时间也不多)
- 用拓扑排序删去一部分点(入度为0,或出度为0的点可以删去,直到没有满足条件的点)
- 搜索,剪枝(深度在3-7之间、每次访问的结点ID都应该大于起点的ID),这里有很大的优化空间
- 关于换行应该是CRLF(\r\n)还是LF(\n),官方表示都没有关系
先贴一个在100万环【E28W N30000 A1004812】的数据集上要跑几分钟的Baseline(复赛中相邻边权的if判断自己加上吧)
上分小窍门:
- 群消息收益占比 60%
自己优化收益 30%
服务器抖动收益 10% - XJBG收益 100%
- 吃瓜 999%
- magic 1000%
初赛基本方案与成绩:
方案:正4步负3步DFS,按照28Wx50适配
成绩(我太菜了):0.211
复赛题目差异:
- 转账记录 28W -> 200W
- 环个数 2914186 -> 2000W
- 转账金额比例约束 :如[A,B,X],[B,C,Y],要满足0.2 <= Y/X <= 3
- ID为32位的无符号整数,转账金额为32位的正整数,且都小于2的31次方,选手可以不用考虑范围之外的输入。(转账金额不为0的意思,但所有数据依然为int32范围)
- 去除了平均转账记录数< 10的说明
- (你就是这么用第三个数据的???)
- (为什么转账记录x7,环的个数也才x7,有一种291W复制粘贴几次的赶脚)
复赛规则差异:
- 每天3次,AB榜,最后一天下午更换数据集,且对题目进行调整(喵喵喵???),可提交5次
- 禁止直接根据ID和权重的取值排除部分数据,禁止跳过文件中的部分数据
水群的快乐>打其他比赛的快乐>>造数据的快乐
提供初赛几组数据的复赛结果,仅供参考:
- [56]->[41]
- [3512444]->[697518]
- [38252]->[9153]
- [2861665]->[639096]
鉴于要结果的同学太多了,发布一个复赛baseline(慢到家了):
暴力对表程序,仅限挂机拿手环使用,跑出Family群( 原热身赛1群,群号974891831)中1963W随机图的时间约一小时,跑初赛数据约8分钟。
200512.比赛结束前的最后一次更新:
0509官方更新了A榜使用的数据集,就目前的结果来看,在线的数据分布应该是菊花图、随机图、完全图的组合(没验证过,没啥意思)。交易金额没有明确表示是否存在0,但是在线数据在环中不存在两次连续0的转账记录。
之前造1963W数据的策略为:
- 转账金额随机正整数
- 结点ID为[0,111314)全覆盖,转账记录数为200W
- 转入与转出概率相等
- 6000,25123开始的连续ID分别组成K13
- 10000开始的连续ID组成K14
- 剩余边按照上述描述随机生成
生成随机图与完全图并不难,简单说一下生成无标度网络的一种方法(参考networkx):
- 假定每次加入一个新的结点和一定数量的边(通过概率比例调整,见例),当现有结点有越高的度时,其与新加入结点连接的概率越高
- 每次按照alpha+beta的概率产生一个新结点并连一条边
- 按照gamma的概率在旧的结点之间连一条边
- 满足alpha+beta+gamma=1,alpha表示按照入度分布连边(入度占总入度比越高的点越容易被选中),beta表示按照出度分布,gamma表示按照入度和出度分布
- 说明:假定按照入度选中的点为v,按照出度选中的点为u,新结点为x,边权重为w,则每次有alpha概率产生[x,v,w],有beta概率产生[u,x,w],有gamma概率产生[u,v,w]
- 重复上述步骤直到边数满足要求
- 例:结点N=100,边E=500,则alpha+beta=0.2,gamma=0.8
- 不可以有自环,边权为随机正整数
- 为了避免ID集中在某个范围,在int32范围内做ID重映射
复赛成绩/总结(2020.05.16):
方案:正4步负4步DFS,按照题目给定范围适配
成绩:2.8213
这比赛好迷呀,之前花了这么久,对问题做了尽可能好的(划掉,就是暴力DFS嘛)适配,代码已经特化到很难修改了。下午改了将近两小时才有成绩,很能理解很多大佬们五连WA感到愤怒的原因。
总体而言本次比赛初赛和复赛阶段的收获比较有限,最大的宝藏就是认识了一群优秀的同学,大家一起交流吹水十分快乐,感谢感谢!
参考资料:
1.数据生成.https://github.com/byl0561/HWcode2020-TestData
2.测试数据.https://github.com/liusen1006/2020HuaweiCodecraft-TestData
3.ddd的热身赛代码/初赛数据集分析.https://github.com/justarandomstring/2020-Huawei-Code-Craft
4.解读幂律分布与无标度网络.https://mp.weixin.qq.com/s?__biz=MzIzMjQyNzQ5MA==&mid=2247491852&idx=1&sn=72c05f43448b455a8937ee60e8a443cf
5.无标度网络生成与可视化.https://networkx.github.io/documentation/stable/reference/generated/networkx.generators.directed.scale_free_graph.html