USACO 5.3 章节
相关讲解可在USACO上看原文,也可以搜索nocow找到翻译的! (nocow上有些微翻译是有问题的,如果想看nocow翻译的建议也对着英文看)
以下记录以下 自己之前未掌握的一些要点,以及按自己的括号表述的形式来记录。
USACO Section 5.3 启发式搜索
启发式搜索的主要思想是通过评价一个状态有”多好”来改进对于解的搜索.
0<= 可行的估价函数 <= 实际代价
启发式剪枝: 若搜到最小值,已经搜索到的最优值为C,当前的代价为A(从起始状态到这里的实际代价),启发的期望剩余代价为B(从当前点到目标的估价),如果
A+B>C
也就是期望总代价比已经搜索的还大,那么剪枝,注意到上面有提到:估价函数是小于等于实际代价,也就意味实际代价>=A+B>C
,所以可以剪枝Best-First Search最佳优先: 深搜 中,在子节点访问顺序部分做估价处理,调整搜索顺序,再结合上面的剪枝
A*
可以和第二条相对,看做广搜中加入了顺序估价。
关于估价函数
如果为0,可以看做毫无优化的默认算法。
如果为实际代价,那么就直接可以最快向目标前进。
Milk Measuring - milk4
题意
从P个数中选 取任意个数,使得它们的整数倍数的和=Q
如P: 3,3,5,7
Q:16
16=3*2+5*1
目标,1选的数的个数尽量小,2在个数尽量小的时候,字典序最小
1<=Q<=200000
1<=P<=100
1<=每个数<=10000
输出选择的方案
通过不了的题解
emmmm 我为什么一看就是个sort(从大到小)+dp[当前第几个数][和的值]= {最小选取的个数,最后选择的index,选择的个数,倒数第一次选择的index}
空间O(Q*P)
,时间O(Q*P)
能得到最小的个和方案,
dp时 优先个数(目标要求个数),其次上一次的index(因为sort过 且目标要求字典序),个数用来反向输出方案
1 |
|
然而不幸的是超空间限制了
Execution error: Your program (
milk4’) exited with signal #11 (segmentation violation [maybe caused by accessing memory out of bounds, array indexing out of bounds, using a bad pointer (failed open(), failed malloc), or going over the maximum specified memory limit]). The program ran for 0.000 CPU seconds before the signal. It used 31552 KB of memory. `
优化可以优化掉p,在实现记录的时候用指针的方式,没有尝试能不能改过
可通过的解
IDDFS 迭代加深搜索,使用场景,在低的层级找到解就是最优/目标解。
和广搜的区别是,广搜过程中会用内存记录,而迭代加深每次都是深搜,但是逐次增加深度。
可行性:每次加深深度,新的状态和上一层的状态是数量级差异,所以其实只和最后成功搜索到的层数的数量级相关。
综上,IDDFS有着接近广搜的性能,有着接近深搜的空间消耗。
实现pass如下
1 |
|
Window Area - window
题意
在电脑上窗口的操作,5种
- 新建窗口(标识符,x1,y1,x2,y2)
- 置顶t(标识符)
- 置底b(标识符)
- 删除d(标识符)
- 输出窗体可见百分比s(I) ,询问数<=500
窗体个数上限(2*26+10=62)
坐标范围[1->32767]
题解
那么显然 横纵坐标只有((2×62)^2)
个,离线+离散+没了?
1 |
|
emmmmmm 在第11个点的时候 出现了唯一标识复用的情况,也就是说 先创建 再删除 再创建,所以期望的边数也就不只 (26*2+10)*2
,我这里尝试的是开到240*240
才能过
所以基本上是 离散化 O(长乘宽(分化的区块个数)*62(每次操作代价)*(t+b+r操作个数)+长乘宽(分化区块个数)*1(操作代价)*(w+s操作个数))
以上代码还可以优化的地方:
通过预处理 真实值到离散值,让每个这样的计算只发生一次。
再建立一个表来优化top,bot,rm到接近O1每次,额外的指针访问时间,缺点是1增加空间,2本身数据不大的情况,枚举查找的效率是不差的
然后有一些博文有 两个矩形之间处理的优化,也就是 不分成9块,而是分成最多5块,类似旋图,然后平时只记录相对的层数(z-index),然后在查询的时候,才自值开始向上查找。
Network of Schools - schlnet
题意
有向图,(N<=100)个节点
- 求最少选取多少个点,通过这些点能够沿着有向边到达所有的点
- 求最少加多少边,让真个图变成全连通
题解
emmmm一眼
- 直接求强连通,然后缩点,然后按照出度排序,从0开始删,直到为最后只剩独立点,进行统计?
- 把这些缩点后的 max(出度为0的点,入度为0的点)
强连通直接tarjan
1 |
|
Big Barn - bigbrn
题意
NxN(N<=1000)矩阵A点上值有0或1
1的个数<=10000
求最大的全0正方形(不能斜着)
输出边长即可
题解
这感觉二分答案?因为二分的话如果大的可行,那么小的必定可行
又必定正方形的左侧和上侧都有点(边界看做全是点)
假设存在一个最优解左侧没有相邻点,则可以向左平移直到有点,对上侧同理。
emm 这样想下去,暂时没想到一个时间复杂度内能完成的解法,再看又像是二维线段树,跟着2维线段树的思路联想到前缀和的类似的矩阵处理
假设当前测试的长度为len
那么用B[i][j]
表示 A[i->i+len-1][j]
为1的个数
C[i][j]
表示A[i->i+len-1][j->j+len-1]
为1的个数,即是C[i][j->j+len-1]
根据前缀和类似的算法,A->B
可以在O(N^2)
完成B->C
也同理,综上O(N^2*log(N))
然而很不幸的是 usaco给的空间是真的有限,就算换成 new 也会在第11个点挂掉,所以bit压位,然而事后发现,可以不用存储C,直接判断C的值就好了,从空间O(3N^2)
变为O(2N^2)
就能过
1 |
|
总结
emmmmm所以这些题目和标题的启发式搜索的关系是?