Noip2008前三题是基础题,仔细一些都是可以AC的,第四题的证明很巧妙,但是看懂后代码其实很简单,感觉在这些大家都不屑去做的简单题中又学到了不少,四道题代码基本都是十几二十行就够了,渐渐感觉到,比代码和算法更重要的是思想与建模,觉得下阶段应该多注意培养自己的建模能力。
T1:火柴棒等式
最简单的模拟题,首先记录下每种数字需要的火柴棒数,最后枚举验证即可。
+ ?
1 2 3 4 5 6 7 8 9 10 11 |
|
T2:笨小猴
基础能力题,统计字符出现次数,然后直接用最简单的素数判断即可。
+ ?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
T3:传纸条
对于这道题,其实不需要考虑从下往上传,直接考虑两条纸条从(1,1)开始传就可以了,可以采用动态规划,因为离开某个点之后,便不可能再回来。并且在转移时,判断同时转移的两点是否相同,若相同,不加数字,所以一定存在比其大的走法,不会影响结果。所以用四维Dp即可完成。
+ ?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
然后可以考虑Dp的维数优化,因为纸条传的横坐标+纵坐标=走的步数,所以可以以步数为媒介将其优化为三维,代码如下:
+ ?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
注意界限k+2-i>=1&&k+2-j>=1
T4:双栈排序
首先给出结论P: S[i],S[j]两个元素不能进入同一个栈
等价于
存在k,满足i<j<k,使得S[k]<S[i]<S[j].
证明:
考虑对于任意两个数q1[i]和q1[j]来说,它们不能压入同一个栈中的充要条件是什么(注意没有必要使它们同时存在于同一个栈中,只是压入了同一个栈).实际上,这个条件p是:存在一个k,使得i<j<k且q1[k]<q1[i]<q1[j].
首先证明充分性,即如果满足条件p,那么这两个数一定不能压入同一个栈.这个结论很显然,使用反证法可证.
假设这两个数压入了同一个栈,那么在压入q1[k]的时候栈内情况如下:
…q1[i]…q1[j]…
因为q1[k]比q1[i]和q1[j]都小,所以很显然,当q1[k]没有被弹出的时候,另外两个数也都不能被弹出(否则q2中的数字顺序就不是1,2,3,…,n了).
而之后,无论其它的数字在什么时候被弹出,q1[j]总是会在q1[i]之前弹出.而q1[j]>q1[i],这显然是不正确的.
接下来证明必要性.也就是,如果两个数不可以压入同一个栈,那么它们一定满足条件p.这里我们来证明它的逆否命题,也就是"如果不满足条件p,那么这两个数一定可以压入同一个栈."
不满足条件p有两种情况:一种是对于任意i<j<k且q1[i]<q1[j],q1[k]>q1[i];另一种是对于任意i<j,q1[i]>q1[j].
第一种情况下,很显然,在q1[k]被压入栈的时候,q1[i]已经被弹出栈.那么,q1[k]不会对q1[j]产生任何影响(这里可能有点乱,因为看起来,当q1[j]<q1[k]的时候,是会有影响的,但实际上,这还需要另一个数r,满足j<k<r且q1[r]<q1[j]<q1[k],也就是证明充分性的时候所说的情况…而事实上我们现在并不考虑这个r,所以说q1[k]对q1[j]没有影响).
第二种情况下,我们可以发现这其实就是一个降序序列,所以所有数字都可以压入同一个栈.
这样,原命题的逆否命题得证,所以原命题得证.
此时,条件p为q1[i]和q1[j]不能压入同一个栈的充要条件也得证.
这样,我们对所有的数对(i,j)满足1<=i<j<=n,检查是否存在i<j<k满足p1[k]<p1[i]<p1[j].
二分图的两部分看作两个栈,因为二分图的同一部分内不会出现任何连边,也就相当于不能压入同一个栈的所有结点都分到了两个栈中.
此时我们只考虑检查是否有解,所以只要O(n)检查出这个图是不是二分图,就可以得知是否有解.
此时,检查有解的问题已经解决.接下来的问题是,如何找到字典序最小的解.
实际上,可以发现,如果把二分图染成1和2两种颜色,那么结点染色为1对应当前结点被压入s1,为2对应被压入s2.为了字典序尽量小,我们希望让编号小的结点优先压入s1.
又发现二分图的不同连通分量之间的染色是互不影响的,所以可以每次选取一个未染色的编号最小的结点,将它染色为1并从它开始DFS染色,直到所有结点都被染色为止.这样,我们就得到了每个结点应该压入哪个栈中.接下来要做的,只不过是模拟之后输出序列啦。
还有就是注意复杂度的优化,如果对于数对(i,j),都去枚举检查是否存在k使得p1[k]<p1[i]<p1[j]的话,那么复杂度就升到了O(n^3).解决方法就是,首先预处理出数组min,min[i]表示从p1[i]到p1[n]中的最小值.接下来,只需要枚举所有数对(i,j),检查min[j+1]是否小于p1[i]且p1[i]是否小于p1[j]就可以了.
+ ?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
注意点:
1.Dp特别注意边界问题;
2.Dp维数优化可以通过找中间量来实现,比如T3中的步数;
3.注意是puts("0")而不是puts(0),否则会结果错误,但是编译可通过;
4.不得不提自己又被双等号坑了一次,特别注意!!!
Noip2008提高组总结,布布扣,bubuko.com