NOIP 2003解题报告
第一题(神经网络):
题目大意,给出一些点,每个点都有2个值,c和u,给出一些有向边,权值为w。入度为0的点的c已知,其它点的c未知,每个入度不为0的点node的c等于sum(c[k]*w[k][node]),k为与它相连(有向边指向node)的点。求所有出度为0且c>0的点。
解题过程:
此题的方法应该很多,比如拓扑排序,BFS等;我直接写了个递归,要求某个点的c,就先访问所有与它相连的点,递归下去直到 碰到入度为0的点。。 较麻烦的送分题,30分钟AC
第二题(侦探推理):
明明同学最近迷上了侦探漫画《柯南》并沉醉于推理游戏之中,于是他召集了一群同学玩推理游戏。游戏的内容是这样的,明明的同学们先商量好由其中的一个人充当罪犯(在明明不知情的情况下),明明的任务就是找出这个罪犯。接着,明明逐个询问每一个同学,被询问者可能会说:
证词中出现的其他话,都不列入逻辑推理的内容。 明明所知道的是,他的同学中有N个人始终说假话,其余的人始终说真。
现在,明明需要你帮助他从他同学的话中推断出谁是真正的凶手,请记住,凶手只有一个! 如果你的程序能确定谁是罪犯,则输出他的名字;如果程序判断出不止一个人可能是 罪犯,则输出 Cannot Determine;如果程序判断出没有人可能成为罪犯,则输出 Impossible。
解题过程:
这题真心恶心,代码写的老长,字符串的处理超恶心,刚写完时连样例都过不去,结果发现好多小错误,比如保存变量时,题目是总共m人,n人说谎,我比较习惯用n表示总人数,结果枚举罪犯的时候循环里就写成n了。
基本算法:枚举罪犯和今天是星期几,然后依次判断每个人的话,去掉废话,记录说谎的人的总数,根据说谎的人的总数来判断这种假设是否可能成立。
要点1:题目说 “有N个人始终说假话”,“始终”两个字非常关键,因此还要记录每个人之前是否说了真话或谎话或者未确定。。如果某个人又说真话又说假话,那么这种情况也是要排除的。
要点2: 如果说谎话的人数小于n,这种情况也是可行的(一开始写成如果liars!=n就return)。
要点3:还要记录说真话的人数,如果大于m-n也是不行的。。
要点4:如果有多个假设成立,不能马上下结论嫌疑犯不止一个人,因为有可能是2个假设的罪犯是同一个,但是星期几不同。 折腾了一个下午,yzoi死都过不去,其他OJ就AC了。
第三题(加分二叉树):
设一个n个节点的二叉树tree的中序遍历为(l,2,3,…,n),其中数字1,2,3,…,n为节点编号。每个节点都有一个分数(均为正整数),记第j个节点的分数为di,tree及它的每个子树都有一个加分,任一棵子树subtree(也包含tree本身)的加分计算方法如下: subtree的左子树的加分× subtree的右子树的加分+subtree的根的分数 若某个子树为主,规定其加分为1,叶子的加分就是叶节点本身的分数。不考虑它的空 子树。 试求一棵符合中序遍历为(1,2,3,…,n)且加分最高的二叉树tree。要求输出; (1)tree的最高加分 (2)tree的前序遍历 解题过程: 白书上原题,经典dp模型。。枚举区间里的根, 然后就分成了2个区间,就变成了2个子问题,取最优情况即可。
第四题 (传染病控制):
近来,一种新的传染病肆虐全球。蓬莱国也发现了零星感染者,为防止该病在蓬莱国大范围流行,该国政府决定不惜一切代价控制传染病的蔓延。不幸的是,由于人们尚未完全认识这种传染病,难以准确判别病毒携带者,更没有研制出疫苗以保护易感人群。于是,蓬莱国的疾病控制中心决定采取切断传播途径的方法控制疾病传播。经过 WHO(世界卫生组织)以及全球各国科研部门的努力,这种新兴传染病的传播途径和控制方法已经研究消楚,剩下的任务就是由你协助蓬莱国疾控中心制定一个有效的控制办法。
研究表明,这种传染病的传播具有两种很特殊的性质; 第一是它的传播途径是树型的,一个人X只可能被某个特定的人Y感染,只要Y不得病,或者是XY之间的传播途径被切断,则X就不会得病。 第二是,这种疾病的传播有周期性,在一个疾病传播周期之内,传染病将只会感染一代患者,而不会再传播给下一代。 这些性质大大减轻了蓬莱国疾病防控的压力,并且他们已经得到了国内部分易感人群的潜在传播途径图(一棵树)。但是,麻烦还没有结束。由于蓬莱国疾控中心人手不够,同时也缺乏强大的技术,以致他们在一个疾病传播周期内,只能设法切断一条传播途径,而没有被控制的传播途径就会引起更多的易感人群被感染(也就是与当前已经被感染的人有传播途径相连,且连接途径没有被切断的人群)。当不可能有健康人被感染时,疾病就中止传播。所以,蓬莱国疾控中心要制定出一个切断传播途径的顺序,以使尽量少的人被感染。
你的程序要针对给定的树,找出合适的切断顺序。
解题过程:
1.一开始感觉是树形dp,然后就朝着树形dp的方向去想,对于某个节点,切断某个儿子,处理剩下的儿子不就是子问题了么 。然后就几下写了个O(n)的算法,自己测了几个数据都对到了,于是信心满满地提交,结果只过了5个点。 仔细想想发现这些子问题是不能同时进行的。因此这种算法只适用于二叉树(自己出的数据都是二叉树。。),竟然能过5个点。。
2.实在想不出dp方法,百度了一下竟然说是搜索题。然后就自己写了个搜索。。由于传染病每次往下传一层,而每一层都只能断掉一个,所以可以以层为状态DFS。一开始预处理出每一层有哪些点,然后DFS,枚举断掉的点,用一个数组flag表示某个点是否已经断掉,记录被传染的人数或者枚举断点的时候要先看看它到根的路径中有没有点已经断了,如果断了,这个点就不能算进被传染的人数,或者再去把这个点断了。 一开始不敢写搜索是因为n<=300.。 程序写起来挺简单,40分钟写好,修改2次后AC;
收获:
1.树形的题目不一定就是树形dp,做题不要死钻一种算法,要从多角度去想,大胆去做。
2.字符串的处理要灵活,char数组和string哪个方便用哪个;
3.充分琢磨题意,比如第2题侦探题的“始终”2个字,还有第一题最后只要求输出 输出层(出度为0的点),一开始都搞错了。