1.7 光线切割
转换为逆序数问题
1.8停电梯问题
自己想的:重心法
书上:类记录表法,停的位置每往上一层,需要多走的人次
进阶:电梯在k个楼层停
或者说,并不是所有的人都需要坐电梯的,实在很低的楼层就直接走上去了,因此初始问题实际上也是一个双层的问题
1.9 见面会
思路:转化为染色问题,针对染色问题思路,尝试用K种颜色进行染色,逐渐增大K的值。查询邻接顶点颜色表
进阶:集中安排
1.11 排石头游戏
思路:类似于小时候玩的三五七
进阶:动态规划+记录表还可以解决
(1)先取完者失败的问题
(2)每次可以取1-k块问题
实际上是拈游戏(NIM)的一种,可以推广为n行,每次取k个
1.13两堆石头
思路:求质数所用的筛选法
记录表法是从一个合理的基础推导出所有合理的可能解
而筛选法则是从排除项的基础推导出所有排除项
进阶:NIM4
(1)第一个玩家不能拿光所有
(2)每次最多只能拿前一个所拿的两倍
过程并不是独立的,受到上一次情况的影响
条件(2)限制的是每次向前搜索的范围,所以才会出现斐波那契数列的情况
综合1.11-1.13
对于这些双人博弈的问题,向前推理的过程中存在先手和后手转化的情况
1.15 数独
快速构造数独的方法
貌似我还有一个没写完的解数独程序
1.16 24点
分治法在于子问题的求解与顺序无关
因此可以用二分法将速度加快到log级别
2.1 二级制中1的个数
1、原数减一相与可以减少一个1的数量
2、查表法,以空间换时间
3、对于32位数,可以拆成4个8位二进制再查表,这就是子问题与顺序无关的一个代表
2.2 阶乘
N!中含有2的质因数的个数,还等于N减去N的二进制中表示1的个数,转化为2.1
2.3 水王
问题:发帖数超过总帖子数的一半
1、先排序,无论如何N/2的帖子必为水王的
2、同时删除两个不同的ID
推广:3个人超过1/4,同时删除4个不同的ID
2.4 1的个数
个十百位分别计算1的个数
2.5寻找最大的K个数
关键:不需要管找出来的K个数内部顺序,是排序算法的一个退化算法
1. 参考快排
2. 根据比K大的个数二分
3. 要求最大的K个数,先随机营造这样一个集合,再不断淘汰小数(可以借助堆的数据结构)
4. 计数排序(记录表法实乃空间换时间的典范,但如果数的范围很大,则空间浪费了,时间也没有省)
http://www.cnblogs.com/kaituorensheng/archive/2013/02/23/2923877.html
5. 基数排序
排序算法稳定性的意义:分级排序
2.6 精确的浮点数
关键:循环小数的处理
乘以10^m,m为循环小数的位数
2.7 最大公约数
- 最大公约数:辗转相除法(递归)
- 针对大整数取模运算的开销,可以采用辗转相减法。
- 移位运算可以低开销代替以2为除数的除法运算。
具体运算过程:x,y能/2就/2,不能/2再用辗转相除法。
2.8 找到NM乘积只含01的最小M
两者对接问题,搜索M运算量太大,但是乘积是由明显特征的,可以减小搜索量
同余的只保留一个
直观理解:
1
10 11
100 101 110 111
假设10与11同余,则11下面的所有子树都可以不考虑。
证明:
10s≡10s+(t−s)≡10s+2(t−s)≡...≡10s+(N−1)(t−s)(modN)
10s+10s+(t−s)+...+10s+(N−1)(t−s)≡0(modN)
扩展问题2:在原问题的基础上继续向下迭代
2.9斐波那契数列
xn+1=xn+xn−1
可以由特征方程 x2=x+1
解得 x1,x2
则通项公式为 Fn=Axn1+Bxn2 待定系数即可矩阵递推,方便表示2的幂次结果,采用二进制分解
(Fn,Fn−1)=(Fn−1,Fn−2)∗A
其中: A=[1110]
2.11寻找最近点对
分治法没毛病,但是在left与right之间最小值时充分利用左右部分已经求得的最小值,化简问题。
两个正方形内部才是可能有效解出现的位置。
扩展问题:
1. 求相邻最近数对最大差值
普通思维:先排序再相减
O(nlog2n)+O(n)
利用抽屉原理,减少桶排序所需要的空间,求桶最大值与下一个桶最小值之间差值最大值,对于部分数据较为集中的数据较好
2. 求最大距离点对
(1) 求凸包:
卷包裹法(每次遍历所有剩余点求得一个有效点)
Graham Scan(先排序,时间复杂度
O(nlog2n)+O(n)
)
(2) 求最大距离:3点到直线的最大距离,点与直线都是逆时针旋转的
参考:http://blog.csdn.net/hackbuteer1/article/details/7484746
2.12 给定一个数N与数组S,求S中最接近N的子集
- 递归
list1.push_front(n); //典型的01背包问题
find_factor(sum-n, n-1); //放n,n-1个数填满sum-n
list1.pop_front();
find_factor(sum, n-1); //不放n,n-1个数填满sum
这里因为是从1~n取,所以用了n直接传入n-1即可,对于任意数组应该传递剩余数组。