转自:http://blog.csdn.net/littlekid/archive/2008/07/19/2677373.aspx
July_1
POJ1240 Pre-Post-erous!
http://acm.pku.edu.cn/JudgeOnline/problem?id=1240
对于一棵m叉树,只知道先根序遍历和后根序遍历序列是不能得到确切的树的结构的,但是可能的树的结构有多少种?
递归地推:
如果根有n棵子树,则共有C(m,n)种选择;
然后再乘以每棵子树可能的结构数目:乘法原理。
根据先根序遍历和后根序遍历序列可以划分子树和得到当前结点有多少子树,这个不难想。
July_3
POJ Picnic Planning
http://acm.pku.edu.cn/JudgeOnline/problem?id=1639
度限制最小生成树。
lrj《算法艺术与程序设计竞赛》上有比较清楚解法,我看懂后照着敲了一个,结果花了一个下午才写出来,整整300+行(我写代码向来冗余,待整理),好在做出来了。
我的解法思路(大致照lrj的解法):
1 先Prim+heap求最小生成树块;
2 然后添加0到各连通块的最小边;
3 然后通过h(t)求h(t+1)——关键部分:
枚举2中未选的0的邻边,然后在形成的环中找最大边删除(这里跟lrj的不一样,我作了一遍dfs找最大边,最后删边也用dfs实现)。
终止条件为0的度数到达k或上边的情况不能使生成树总长减小。
这套方法太麻烦,由于题目数据范围小,可以考虑换Kruscal实现。
July_4
POJ1041 John's trip
http://acm.pku.edu.cn/JudgeOnline/problem?id=1041
经典欧拉回路的题。
这个题目是为了攒一份欧拉回路标程写的——这个题目是求欧拉回路而且多两项要求:一是起点必须是第一条路径的较小标号点,二是求出来的回路序列要是最小(每条路径有一个标号)。
原来在USACO写过一个求欧拉回路要求访问点序列最小的的欧拉路,当时用矩阵实现,这样欧拉路/欧拉回路基本会写了——不过如果要非递归实现的话……
这个题目写了半个上午——毕竟要求序列最小这一点换了好几种想法……
July_12
炮兵阵地
source Noi2001 day2
http://acm.pku.edu.cn/JudgeOnline/problem?id=1185
这个题基本算我写的第一个状态压缩DP的题目。
原来的时候把状态压缩DP想得很高深,一直不敢动,今天下定决心通过以前看过的一些思想的总结来写这一类题目,写完这个题后初步感觉状态压缩DP的主要特点就是状态压缩(废话!但理解这句话用了这么几个月):除了对状态的表示要压缩起来表示之外与普通DP没什么两样。
这个题目是个赤裸裸的状态压缩DP:直接按照题意状态转移就可以了——用二进制表示地形和当前行炮兵布局,一个状态用两层表示:上一行和当前行的炮兵布局。难想的一点是状态怎么压缩:其实可以用数组把一行的合法布局记录下来,这样100列也就60种状态,记录两行状态数不过3600,复杂度O(n*num^3)(num为状态数),最大100*60*60*60=216000,加上一些判断其实很快的。
Wa了一次,忘记一直取最大值了。
Jul_13
POJ2817 WordStack
http://acm.pku.edu.cn/JudgeOnline/problem?id=2817
经典状态压缩DP。
求最长最长汉密尔顿路(非回路)。由于昨天开始做了几个状态压缩的题目后,对这类题有点理解,加上这个题目基本上是赤裸裸的状态转移,很快写过。
Wa了一次:由于开始求的是最长公共子序列长度(其实是最大对应相同的字母数)。
July_14
POJ 3164 Command Network
这个题目就是经典的最小树形图。
前几天看到这个问题的朱刘算法好像很容易理解、很清晰,加上今天做题不顺就写了。
TJU 2248 也是求最小树形图,先做的这个(这个赤裸裸!)
http://acm.tju.edu.cn/toj/showp2248.html
写了一个下午,原因是头脑不清晰,代码风格也不好,写来写去开始一直TLE,后来找了hdu的一个人的标程,发觉自己的思路没错,基本一样……
查了好久,发现在找环后标记时标记了缩点为已经从图中去除!
改了后WA,错误在于缩点时入边出边不分——对于入边要减去dis[ pre[u] ][u]!
还有网上推荐的uva11185也过了
http://icpcres.ecs.baylor.edu/onlinejudge/index.php?option=com_onlinejudge&page=show_problem&problem=2124
总结下做法:网上大家贴的算法都很清晰,有个图更是明了,不过代码还是长了点。。
wy的总结:
http://hi.baidu.com/wywcgs/blog/item/a1ce10f4a8fa366fdcc47462.html
算法图解:
http://p.blog.csdn.net/images/p_blog_csdn_net/huangwei1024/tree_graph.JPG
July_15
POJ3368 Frequent values
我做得最暴力的线段树了!本来想试试结果过了。标准解法是RMQ的st算法。
其实感觉这两个东西还是很相像的,觉得如果不是卡内存时间过紧的话,一般的RMQ(就是我目前能作的白痴RMQ)都可以用线段树干掉。
太暴力了,线段树上直接维护了最左边和最右边的值和出现次数和本区间的最大重复值的次数——5个数值!!!然后用RMQ的方法合并,就跟我们正常思维一样,新合并区间的最大数值出现次数会是左右节点和中间数值的重复次数中最大的一个,思路清晰就好写了。
POJ3321 Apple_tree
http://acm.pku.edu.cn/JudgeOnline/problem?id=3321
第一次做树状数组的题目,不过值得学习的却是树遍历的特点。
这个题目开始以为用线段树做(其实是可以的!) 由于想了很久也没有想到怎么来将树的结构排序,就看了Discuss,发现树状数组也可以——想到从没写过,然后就去找文章看……
题目关键不在数据结构(用的是标准的树状数组或线段树),关于排序的思想很重要:先根序遍历,对每个节点记录其序号和其子树的最大序号(也就是标记一棵树遍历的第一个和最后一个节点的遍历序号st、ed)。
统计一个节点为根的树中数目时,直接计算1到st-1、ed的数组和然后相减即可。
POJ1204 Word Puzzles
这个题目其实还是个简单题——直接写个Trie就好了。不过我还是RE了不少次数:原因是忘记初始化指针指向NULL了!!!!弱智啊~~
不过做了这个题目我在POJ上的little id-->R2D2的题数就与littlekid这个帐号一样了。将两个号一合计:在POJ上也算AC了有283题了,+U,fighting!
July_16
POJ1037
动态规划+递推。
这个题目去年第一次看到就推出了那个算n个栅栏以第i短的为第一根下一根放短的or长的栅栏的方案数。
f[n][1][0] = 0; f[1][1][1] = 1;
f[n][i][0] = f[n-1][i-1][0]+f[n-1][i-1][1];
f[n][i][1] = f[n][n+1-i][0];
麻烦的是求具体方案。
求第一根栅栏还是很好求的——直接从f[n][1][0]、f[n][1][1]……一直减,找到i和类型(短/长)。
维护一个集合为未选栅栏
中间的递推比较麻烦:若类型为短,从1减、否则从上次得到的i向上减。(-f[t_len{后边长度}][i][type])。
找到位置i后输出第i小的数。
总之这个题目很考递推能力——推了几个小时还是错了一点,最后discuss里那个人的程序递归实现的还是帮了大忙!
这个题目思想很好,以后要回来温习。
July_16
POJ 1077 Eight
经典搜索题。
这个题目可以用一般BFS,A*,双向BFS……等方法做。
原来写过BFS版本,今天写了个双向BFS——毕竟听说过这个名词以来还没写过。
其实双向BFS真的很简单:就是BFS的时候同时从目标和初始状态做BFS,关键的地方在于判断当前搜索出来的状态是否在另外一棵搜索树中出现(这里借用 Hash判断,对每个状态hash时记录其在队列中位置,利用位置关系判断——比如我开一个队列,初始状态从0向后[head1,tail1),目标状态从最后向前(tail2,head2], 如果一个节点出现在另外一个区间,就找到解了)。
这样0ms,普通600+ms,真的很强大……
不过很费空间(本人不幸0ms用空间最多),下次学了A*再做。
July_17
July_1195
POJ1195 Mobile phones
source IOI2001
经典二维树状数组题。
前边做过一个一维的树状数组题目,就想当然地扩展到二维了,结果郁闷地WA了几次。
找来解题报告看,发现二维的树状数组麻烦多了,要控制的东西还不少:不过目前还是不很明白……
照着别人程序写了个,AC了,
June_2
http://acm.pku.edu.cn/JudgeOnline/problem?id=3026
Borg Maze
最小生成树。
这个题目本来是简单题,结果由于疏忽,写了两个多小时……
犯了一个弱智错误:数组开小了,不过由于G++不报错,得到诡异的错误……
让我想起寒假时候HDU给我不报RE报TLE的事情了。
June_5
POJ1011/WOJ1212 Sticks
经典搜索题。
这个题目做了很久——去年暑假个人赛模拟赛第一次接触,当时没做出来,不过后来模仿别人程序把POJ1011过了,不过WOJ1212却是TLE。
最近几天看了下《算法艺术与程序设计竞赛》上的减枝方法,总算弄清楚了,今天早上写了一下……
大体框架为DFS,最重要的是减枝。
下面是我的一些减枝:
1、要得到的Sticks长度必定为总长度的约数——这个好想;
2、对blocks按长度由小到大排序(原来也排了,当时不知道为什么要排——为了减枝);
3、搜索顺序为:
用blocks去组成sticks,
减枝一:按照组成sticks的最长block排序(由大到小),这个可以证明的,而且剩下的blocks中最长block一定要选择;
减枝二:组成每根sticks时按从大到小枚举,如果可行则当前方案一定是最后方案(理解这个我用了比较久,其实就是如果有另外的另两根短的blocks可替换的方案存在,那么长blocks放在后面也是对的,如果固定在前面,可减少不少);
减枝三:对于长度相同的blocks,如果当前用其中一根组成当前sticks不能得到结果,那么当前的选择要跳过所有的这些blocks(这个从搜索树上理解就好了——画出一棵搜索树,会发现这些blocks为根的子树完全相同);
这样就可以过了,WOJ1212 15+s, POJ1011 0MS.
trick:由于woj1212只卡时间,我用了一个错误的减枝(很弱智,也就不说了)得到了5MS的程序(当然在POJ1011上WA了)。建议做这题要把两个地方的题目都AC了。
要好好地理解这个题目的方法——我学到的最重要的是从状态搜索树的特点上剪枝。
June_5
HUST1019 A dangerous trip
http://acm.hust.edu.cn/thx/problem.php?id=1019
这个基本代表了一类题目了——与校赛的Path(WOJ1352)的最短奇偶路径相似。
方法很简单:
对每个点保存两个值,做两遍最短路:
第一次普通最长路,
第二遍做最短路时,对所有边枚举其缩到一半的情况,具体就是:
Dist[p->v][1] = min(Dist[i][0]+p->len/2.0, Dist[i][1]+p->len)
别的没什么好说的,这个题目是简单题。
June_9
Cashier Employment
http://acm.pku.edu.cn/JudgeOnline/problem?id=1275
差分约束问题。
这种题目难的地方就是建图,这个题目特殊的地方是得到最后的不等式中第23小时不好处理,那么就枚举啦(数据范围不大,二分也是可以的)。
不过有个地方还没懂——我建图后枚举用bellman—ford后如果满足约束就返回true错了,改为冯威的论文上的!=4的约束条件就AC了——有什么trick?
总结一下差分约束问题:先对题目建立模型,然后抽出不等式,再整理成差分约束标准形,最后bellman-ford检查约束条件。 差分约束简单类题目就这么搞了,难的变化大,目前搞不定,需要多见识积累经验……
June_29
Reset Sequence
POJ Monthly June 2008
http://acm.pku.edu.cn/JudgeOnline/problem?id=3609
题目意思是有n种状态和m种命令(2<=n<=8, 1<=m<=16),然后给出一个n*m的矩阵,表示在状态i下面接受到命令j则改变到状态a[i][j]。目标是求一最短序列满足:在任何初始状态下,通过执行这一命令序列后都到达状态0。
今天状态不好(毕竟近20天没写题了),看到这个题目后没有想法,oldmaner也认为只能穷举,然后我写了上去居然WA了(没有TLE),就在考虑是否漏了情况,未果。
后来改模型,居然推出一个状态DP的模型,还好很快由此想到了图论——这个题目是个简单题。
解法:由于n很小,所以可以对在执行了一系列命令后的状态用一个顶点表示(就是一个2进制数标号,对应位为1表示当前的状态——开始时处于1111状态,最后处于0111状态)。
后面就很简单的建图:如果从某一状态u可以通过命令j转换到状态v,则在u和v间连一条边(“权”为1,标志为j)。
最后就是跑一个最短路。
May_26
ZJU2588
http://acm.zju.edu.cn/show_problem.php?pid=2588
这个题目做了比较长的时间,
为了它先学习了一下无向图的桥,不过没有过~~这个题目不一样的地方是有些点之间不只一条边;
今天想到hash判重(这么简单的思想原来怎么就没想到呢?)
MLE两次,hash数组开大了
这之后WA了一次——如果没有桥后输出空行,我的程序为了控制另外一点(末尾没有多余空格)而输出了0
程序目前时间不够快……
72 2008-05-26 15:22:34 00:01.45 23752K C++ littlekid
Delete the comments
http://acm.fzu.edu.cn/problem.php?pid=1604
简单题,直接模拟做
比赛当时wa了,第二天继续做,开始TLE,后来优化了一下就过了:
对于"/*"找其配对的"*/",如果没找到以后就不用再找了。
教训:当时wa的原因是程序程序写得太乱(自己思路不是很清晰)——写代码前想清楚,代码前先上注释
May_28
POJ3159
http://acm.pku.edu.cn/JudgeOnline/problem?id=3159
这个题目是很久以前据开始做了,第一次用O(n^2)的算法自然TLE,后来学了加堆优化的Dijstra还是没有过,都把堆的各种操作优化了,位运算也上了,交了好多次。
昨天上数据结构的时候看到用数组模拟指针,突然想到会不会是用new或malloc太慢,用静态指针?今天写了个数组模拟指针,果然过了,1032ms,还是有点慢——那些100+ms的是怎么弄的,下次请教下~~
May_29
POJ1144
这个题目就是求无向图的关节点(或割点),前几天就写好了算法,不过WA了,然后这几天就一直怀疑
自己的算法写错了,不断找bug,不过找不到~
后来看到Discuss里的数据果然过不了,——
今天上网查了别人的算法,人家非常暴力的算法都过了……不过最后终于发现自己的错误——读入处理错了,看错题了,又一次死在读入处理上——不过这次不是不会处理,而是看错题了
(第一个数代表后面数相连顶点,我弱智地处理了,具体就不说了)
下次不能犯这么弱智的错误
May_31
POJ3352 Road Construction
http://acm.pku.edu.cn/JudgeOnline/problem?id=3352
这个题目第一次看到是在去年暑假组队赛——当时一直想着怎么DFS硬搞(当时除了硬搞什么都不会)。
上一周开始思考这个题目,做了几个割点和割边的题目后开始做这个题目:
用了求割边然后缩点的方法,
结果为(叶子节点数+1)/2。
开始wa了——只是记录叶子节点,忘记考虑根也可能算一个特殊的叶子节点。
记得今年中南赛A题也是忘记考虑根节点情况郁闷了!
POJ3649
模拟题。
这个题加深了我对于模拟题的理解。
关键在于读懂题意,思路清晰。
这个题目的关键在于理解题,然后计算移动:先计算每个图形的最左最右位置,再把
每一格把所有的图形按规则计算向左移动的格数,然后再根据移动的最左坐标建立新
图像,最后根据规则去掉不输出的前导后续空白输出,一直在做算术。
July_22
POJ3662
这个题目很好。
题目意思是对于一个图求一条从源点到终点的路径满足:第k+1长的边最短。
当时想了半天也只想到优先队列广搜,结果自己代码能力挫+比赛中还有另一题卡
着,没有做出来(这样是可以的,NUDT的Alpc55就这么过了,代码能力牛!)。
后来晚上想到二分一个len,然后把距离大于len的边全部赋值为0(其实就是作
Dijstra的时候判断),小于的赋值为1,求最短路是否与k的关系。
图模型的转换博大精深……
July_23
POJ2866 树形DP
这个题思路没什么好说的,关键地方在于看到题目提到几何坐标时不要怕:这个题目
我开始时有点怕,不过看完之后一下就想到思路了——如果开始怕几何那么这个题目
就会很晚出了。
July_23
POJ2547 No Tipping
集合DP。
比赛时候没做出来——当时我们估计了搜索的时间复杂度明显不行,然后还是写了!
其实看到数据<=20应该就要敏感点地知道要集合DP了,想到这里就好办了。
没有trick的题目:不过对数据的敏感和集合DP的思想意识要加强。
July_23
POJ1103 Maze
在TOJ跟ALPC做比赛的题目,当时想出来的——这个题目关键在于建图,而建
图的关键在于标号:按照题意划分区域,‘/’和‘\'分别代表不同的涉及的四个区
域的不同连接关系。
最后可以证明除了边界区域,一个区域与正好两个区域相连通,故建图后先从边
界区域出发dfs将所有能到达的区域标记出为访问,然后再以没有访问过的点开始
dfs走圈计数。
还不错的一道题。
July_23
POJ2169
图模型转换+最短路
题目的模型转化思想很经典:原来给的就是一个图,但是要选择两个相邻(有边直接
相连)的顶点——一个状态,从一个状态可以依照条件通过一定步数转移到另一个合
法状态,
求从一个初始状态到终结状态的最少步数。
转化思想:建立一个新图,将原图的一条边转化为新图中的一个顶点,对于原图中每
个合法的转移建立一条有向边,然后在新图中求s-t最短路。
trick:这个题比赛时我们虽然想到解法,但代码能力问题加卡其他题,并未做出此
题。
赛后在TOJ将它AC后到POJ提交发现TLE,然后在ZOJ发现MLE。
后来发现我用了new,POJ又把卡new了,然后改成数组就过了。
至于ZOJ,目前还是MLE,我用了40000K左右内存,它自由32767k!可见我
的模型还不够好…… 待改进.
通过这个题的经验,又把原来卡的3013也过了。
通过好几个题:3013 3159 2169
发现如果遇到图论题,还是不要用new malloc的好,数组模拟指针还是快很多——
虽然没那么方便。
July_24
POJ1885
线段树模型
今天跟alpc在TOJ做比赛的题目,好题一道。
题目的模型是现椴树,读入单词为插入节点,遇到数字后在前边找节点,再将前
边的单词复制过来到最后一个位置(当前,而且不必真的复制,直接保存一个link
就可以了),并标记找到原单词从列表中去除。
这个题有个trick:输入的文件中最多10000个单词,但是数字却非常多,最后得
到的结果单词数比10000大很多……
July_24 Software Company
二分+DP。
先二分一个时间上界T,然后DP看是否可以在T时间内做完所有子工程。
状态表示及转移:
f[k+1][t][i]表示前k个人都用了时间T工作,第k+1个人工作t时间时完成i个A公
司的项目时最多能完成B
公司项目数目。
f[k+1][0][i] = f[k][T][i];
f[k][t][i] = max(f[k][t-1][i], f[k][ t-cost[k][A] ][i-1],
f[k][ t-cost[k][B] ][i]+1);
dp好想,二分的思想很重要。
July_25
POJ3666
DP+离散化
做得很不爽的一道题——我太sb了,居然被数据蒙骗了,没有想到什么好方法,直到
比赛还剩下20多分钟才想到离散话,加上代码速度慢了点,没有写出来。
对题意的挖掘最重要的一点在于:
一个点的最后高度Bi必定是A数组中的某个高度——可以证明:一个点要么保持原
有高度,要么向上或向下到最接近他的高度。
离散化后就是O(n^2)的DP,加点优化(记录一下前一个点的高度小于(大于)某个
高度的最小值)就好了。
Jul_27
POJ3667 Hotel
很好的线段树题目——一个比赛时留下的怨念:当时大家都没想法,我试着敲这
个题目,结果TLE了。
这个题目我的想法是对的:直接维护线段树(区间树)的查找、删除、添加操作。
由于查找的是大于指定长度k的最左空白的连续区间,故节点有lmax,rmax,cmax三
个值,还有必要的区间长度、范围的信息(这个一个可以推另一个)。
TLE的原因很弱智:对于一个区间整体的覆盖或删除不必先下到子区间,等要改变
子区间时才将区间信息往下传;合并子区间也是麻烦的(不过当时写对了),细细推
敲就可以了。
区间合并和信息下传的思想!
July_29
Sudoku数独
exact cover problem(覆盖模型)
POJ3076 POJ2676 POJ3074
前段看了Knuth的Dancing Links的论文大致了解了一下阿,昨天看了下mmd的集
训论文,试着把Hust OJ上的1017那个extra Cover problem给过了,然后今天就
开始写mmd推荐的sudoku。
数独转化为extra cover的模型(以9*9为例):
行代表每个格子要填的数的可能:9*9*9
列代表限制条件: 81+9*9+9*9+9*9
//限制1:一个格子一个数
//限制2:一列数不重复
//限制3:一行数不重复
//限制4:一个3*3的块数不重复
Jul_30
POJ 2252
一阶方程求解
重点在于对表达式求值
由于一定不高于一阶,可以用复数的表示方法存储。
这类题目都做出套路了:递归。
加深对“编译原理”的理解。
July_30
POJ2677 Tour
经典DP CLRS上的题目 模拟两个人走来解
状态设计:f[i][j](j<i)
f[i][j] = min(f[i-1][k]+dis[k][i]) k=1,2,...,i-2 j=i-1
f[i][j] = f[i-1][j]+dis[i-1][i] j<i-1