noip2008提高组题解
第一题:笨小猴
模拟
第二题:火柴棒等式
搜索
深搜不用说,确定出两个加数然后判断能否拼出等式。
枚举确实不太好搞,因为枚举范围不确定,太大了容易超时,太小了容易漏解。不过这题的数据貌似很温和,我从 0~1000 枚举也能过。
第三题:传纸条
多线程动态规划
跟 2000 年的方格取数很像。
还是看做两个人同时从左上角走到右下角。用 f(i, j, k) 表示横纵坐标和为 i,第一个人的行坐标为 j,第二个人的行坐标为 k 时的最大和。
由于一个地方不能走两次,所以对于除了终点以外的所有 j = k 的情况,其 f 值应等于负无穷。(j=k && i!=m+n && j!=m 才是终点的情况)
第四题:双栈排序
二分图判定
首先想到的是贪心,能拿 30~50 分左右。
然后看了下题解。(参考 https://www.byvoid.com/blog/noip2008-twostack/ 和 http://www.cnblogs.com/vb4896/p/3875833.html )
首先我们要判断一个序列能否进行双栈排序。两个栈,容易让人想到二分图。按照这个思路往下想,二分图中的边所代表的关系很明显是指两个数字不能进同一个栈。那么如何判断两个数字能否进同一个栈?
对于任意的数对 (i, j) 满足i<j且a[i] < a[j],若存在 k 使得 k > j > i 且 a[k] < a[i] < a[j],则 i 和 j 必然不能进入同一个栈。(简单解释一下:因为 k 在 i 和 j 的后面,所以在 k 出栈之前 i 和 j 都在栈内,如果 i 和 j 在同一个栈内,j 必然比 i 后入栈,也必然比 i 先出栈,这样就违背了应有的顺序,所以 i 和 j 不能在同一个栈内)
那么我们只需对于满足上述条件的数对 (i, j) 连一条边,最后染色判断是不是一个二分图即可。
判断数对 (i, j) 是否满足条件,如果用朴素的算法,时间复杂度将是 O(n)。我们每次判断的过程事实上是判断在 j 后面的数字中能否找到一个小于 a[i] 的数字。那么我们只需知道 a[j+1]...a[n] 中的最小值即可。用 f[i] 表示 a[i]...a[n] 中的最小值,则 f[i] = min{ f[i+1], a[i] }。每次对于 (i, j) 只要判断 f[j+1] 是否小于 a[i] 即可。复杂度 O(1)。
由于我们要得到字典序最小的方案,那么很明显应该尽量将顺序靠前的数字安排在栈 1 中。
如果得到的是二分图,进行贪心即可。每次如果可以进行多种操作,选择字典序最小的操作。
(顺便一提,BYVoid 大神的代码是有问题的,可以参加那篇文章下面的第一条评论。)