CCI 复习笔记 9

临时抱佛脚啊,就先抱下这张

递归和动态规划可谓是难点,主要是思维。思维这个东西。。。貌似刷题确实可以弥补,可是我实在没空刷题。不说啥了,明天就又面谷歌了,抓紧吧


这次我保证基本每道题都用python写一下。


9.1 小孩跑楼梯,可以一步可以两步可以三步。


这个很简单,类似的题还有什么几个硬币加起来等于多少之类的。代码不放了,太花眼,总之recursion出来的是3^N, 太慢,可以做一个一维的map。记录对于某一个值,有多少种,map(n)=map(n-1)+map(n-2)+map(n-3), 注意是边界条件,map(0)=1。书上给的那个DP解法,比较符合从递归转到动态规划的思想,但是貌似更加不容易懂


9.2 从一个图的一个顶点有多少种可能走到对角,如果有不能走的点怎么办?计算path,以及得到一条路径


map[i,j]=map[i-1,j]+map[i,j-1], 或者数学上可以算出来,C(n,X)。有不能走的点的情况似乎并没有什么区别。检查一下就可以,得到一条路径这个,用DP反而比较麻烦,比较适合用递归。


书上给的方法是对于一个点,如果存在从起点到我这里的路径,就加进来。因为到了树的最底层就是终点,所以终点先加进来,之后再是能够到终点的某个点,这个可以很安全地append path,因为append出线的前提就是x,y 到起点有一条路径,否则根本就到不了那里。这个地方思考有点难。

另外就是用了cache来消除搜索时候的重复。这个也有一个比较难的地方,就是cache return之后为什么就不用append到path里面去了。愿意其实是,这个算法作为DFS,根本只会找到第一个通道,所以所有True的node必然是第一次touch。


贴个代码

if(cache.has_key((x,y))):
return cache[(x,y)]

#It is impossible if we have a true cache element that has not been used in the solution

if((x==0 and y==0) or Get_One_Path(x-1,y,rock,cache,solution) or Get_One_Path(x,y-1,rock,cache,solution) ):
solution.append((x,y))
cache[(x,y)]=True
return True
else:
cache[(x,y)]=False
return False


9.3 在一个array里面找到A[i]=i,所有的数字都是整数


线性查找必须是可以。之后呢?整数这个很重要。由于是整数,所以可以从mid看起,ifA[mid]

如果数字不是distinct,那么两边都要搜索,其中A[mid]和mid之间的关系可以省略一些检测。


9.4 return all the subset of a set


直接上个代码把,写了半天,这题脑子容易和permutation搞混掉。其实我的做法很简单。对于一个set来说,它的subset就是set[1:]的subset的集合,加上这个集合中每一个元素(也就是set[1:]的每一个subset)和set[0]的总和。

base case是当set里面只有一个元素时候,有两个subset,自己以及空集。一个集合的subset一共是2^N个。

Aset = [1,2,3,4,5]


def find_subset(Aset,solution):
if(len(Aset)==1):
solution.append([Aset[0]])
solution.append([])
else:
find_subset(Aset[1:],solution)
solution_extra = [[Aset[0]]+x for x in solution]
solution.extend(solution_extra)


solution=[]
find_subset(Aset,solution)
print solution
print len(solution)

9.5 return all permutation of a string


先来代码

A=[1,2,3,4,5]


def permutation(A,i,solution):
if(i==len(A)-1):
solution.append(A)
else:
for j in xrange(i,len(A)):
A_cpy = list(A)
A_cpy[i], A_cpy[j] = A_cpy[j], A_cpy[i]
permutation(A_cpy,i+1,solution)


solution=[]
permutation(A,0,solution)
print solution
print len(solution)
import math
print math.factorial(5)


很简短,意思是这样,把每一个element尝试和剩余所有element对调,因此每两个element都有对调的机会,对调完成以后,到了最后,也就是叶子,保存起来。

开始的一个误区只要看到A就存起来,其实应该是不对的,另外j是从i开始,保证了可以出现没有交换的情况,所以原本的A会出现在所有交换都没有发生的时候。


9.6 valid pair of n 个括号


我把这题看做是tree的形态问题,可以理解成,给你n个无差别node,找到所有n个node的tree。

考虑一个subset的问题,map(n) 和map(n-1)的关系是,第n个括号可以出现在之前的任何一个node的parent或者child上,貌似比较复杂,但是书上给的第一种解法就是这样的,不过总是要有重复,只能再检测重复。


第二种做法是考虑直接用半个括号重组。有两种情况去加左括号和右括号,加左括号只要还有就行,加右括号要求右括号数目小于左括号。


9.7 扩散查找,就是找到一片符合条件的区域,比如说一个色块,边界是由色彩变化来决定的。


这题我第一眼就觉得是BFS,中毒太深,有一个point是,recursive的算法容易stack overflow,这个得记住。。。然后所有访问过的都保存,如果遇到不同颜色的,不保存也不进queue,解决问题。


这个题目让我想到一个经典的关于maximum flow的问题,关于图像上去除背景的,比这个要复杂不少,忘记具体怎么弄了。


9.8 零钱组合整钱


乍一看,这题和第一题貌似并没有什么区别。。。

但是实际上,区别还是有的。。。问题是在于,这个题有更高的对称性,5+1 和 1+5 是一个玩意。


所以这道题的recursive算法就很简单,先考虑使用几个最大的,再考虑使用几个第二大的。。。每一层使用一种钱币


然后写完了再看怎么做DP吧,做法是把amount和index,(index就是使用哪种钱币)做一个cache


9.9 八皇后问题,找到所有可能的排列,应该是不用考虑对称


难度现在看上去似乎不大,虽然我记得这个似乎是MIT从前大学一年级的题目。基本上所有可能排列这种题,必须是以传值的方式传到下一层去。因为只有到了最后才知道是不是走不下去了。走不下去整个要丢掉。对于只需要一个path的题目来讲,比如说9.2,问题是不一样的,可以用if作为判断,到了最底层再回溯地添加。就是

if(base or subproblem): add_path; return True

这类题的另外一种解法是,每次探索都添加path,但是如果return false了就再把添加的那个path pop出来。这是当初系主任提供的backtracking的code例子

写上代码,字有点错,不是queue,是queen,queue写多了有点痴呆。

#eight equeue


board_size=8


def find_in_current_board(current_board,num_queue_left):
line_num = board_size-num_queue_left

all_pos = [0 for i in range(board_size)]
for queue in current_board:
line_pos = queue[0]
vertical_pos = queue[1]
all_pos[vertical_pos]=1
if(vertical_pos+(line_num-line_pos) < board_size):
all_pos[vertical_pos+(line_num-line_pos)] = 1
if(vertical_pos-(line_num-line_pos) >= 0):
all_pos[vertical_pos-(line_num-line_pos)] = 1
return [[line_num,i] for i in range(board_size) if all_pos[i]==0] 


def get_positions(num_queue_left,current_board,solution):
if(num_queue_left==0):
solution.append(current_board)
else:
positions = find_in_current_board(current_board,num_queue_left)
print positions
for position in positions:
board_update = list(current_board) #only take the queue's position
board_update.append(position)
get_positions(num_queue_left-1,board_update,solution)




solution=[]
current_board=[]
num_queue=board_size
get_positions(num_queue,current_board,solution)


print len(solution)


9.10 3维数组,必须保证x,y都依次减小,求最大的sum z


这题两维数组自然也是可以。我先说说我的解法,我的解法是,先对x做排序,之后y自然是不对的。但是问题就转变成了找一个二维的最大排序问题


y 1 3 2 4 1.5 5 3.5

z 2 3 4 2 3.0 4 2.0


那么就变成了一个典型的DP问题,需要两个array,一个用来保存对于第i个数字,底部序列是哪个,叫做bottom, 第二个是max_z, 记录用到i的时候,最大值是多大

max_z[i] = max(max_z[i-j]+z[i](if z[i] > bottom[i-j]), running time是O(N^2),


书上的做法是不一样的,书上的做法原则上更好,但是其实不符合至少我心中得DP。。。我心中的DP,数据必须是顺序保存的,但是这本书里面的DP的做法都是cache。Idea是这样,recursion的方式来做这道题的话,Max_with_bottom(i) = Max_with_bottom(j)+z[i] if xy[j]>xy[i]


但是这个running time是很糟糕的(我觉得是N^N),但是不确定


然后剩下的问题就是如何做cache,就是把Max_with_bottom给cache了,然后按reference各级传递。也就是说第一点基本上是正常的recursion,后面大部分的点就被记录了。这题目前在这本书里面我已经见过两次了,绝无仅有的。


9.11 括号组合,使得某个逻辑表达式成立


这题实在有点难,我看题的时候放弃了。解法如下: f(expr,true) 的意思是,这个表达式经过括号变换可以是真的,这个就要求,subproblem是检查在先做某一个operator之后,所以这个整体的idea就是说,先做那个operator后做哪个operator的问题了。


这个也可以做cache。但是我很怀疑可以快多少。


------------------------------------分哥线------------------------------------------------------------------------------

话说TripAdvisor也给面试了,我惊呆了,我招聘会等了太久,干脆把简历一递就走了。。。。,最近运气有点好








你可能感兴趣的:(CCI 复习笔记 9)