您的位置:首页 > 编程语言 > Python开发

算法竞赛第五章例题分析(Python实现):

2016-05-08 14:58 489 查看

例题5-1:Uva10474,非常简单的题目,但是我尝试用python来读取控制台的输入发现并不方便,比如要按照空格分开读取就会很麻烦。。。其实真正的核心代码就5行左右…

def Find_Marble():
def getdata():
data = input()
x,y = data.split()
return int(x),int(y)
def getarray(a):
marble = input()
marble = marble.split()
for i,k in enumerate(marble):
a[i] = int(k)
case = 1
while(True):
try:
n,q = getdata()#获取输入
except:
break
a = [-1]*(n+1)
getarray(a)
a.sort()
print('CASE# %d:'%case)
case += 1
for i in range(q):
x = int(input())
if x in a:print('%d found at %d'%(x,a.index(x)))
else:print('%d not found'%x)
Find_Marble()


例题5-2:这个题如果按照书上的方法并不难,而且用python写代码会很简单,但是书上的方法有一个不好的地方在于它不断的进行push,resize非常的耗时.仔细思考一下,完全可以避开这些操作,方法就是用一个数组来描述每一个木块和相邻木块的关系,比如p[i]代表木块i的上方是木块p[i],block[i]代表第i个位置是哪个木块.(-1代表无木块).

当然这样从编码角度上来说就相对复杂了一点了,具体编码思路有这几点值得说明:

[0]:对于输入的四种指令,我们处理它们的共同部分而不是分成四种情况去处理.

[1]:对于pile函数,我们必须要寻找a的具体位置,然后将a下方的木块k的p[k]置为-1,如果没有下方木块就跳过这个地方,然后还要注意将block[a]也置为-1.(不管a在不在最下面这样写都不会错)

[2]:对于clear函数我们就没有必要使用findblock函数了,因为我们只是需要将a上方的木块归位,而不需要知道a具体在什么地方,这相对于书上的办法就少了一遍查找a位置的过程.效率大大提高.

def Blocks(n):
block = [i for i in range(n)]
p = [-1]*n
def findblock(a):
Final = -1
#首先要找到现在a的位置
for each in block:
if(each==a):
block[a] = -1;return
elif(each!=-1):
k = each
while(p[k]!=-1):
if(p[k]==a):Final = k;break
k = p[k]
block[a] = -1
p[Final] = -1
def clear(a):
if(block[a]==-1):return
while(p[a]!=-1):#遍历a上方的木块
t = p[a]
p[a] = -1#迭代到上方的
block[t] = t#进行归位
a = t
def pile(a,b):
if(a==2):print(p)
findblock(a)
if(a==2):print(p)
while(p[b]!=-1):
b = p[b]
p[b] = a
print(block,p)
def printblock():
for each in block:
if(each!=-1):
k = each
while(True):
print(k,end = ' ')
if(p[k]==-1):print('');break
k = p[k]
else:print('NULL')
while(True):
cmd = input().split()
if(cmd == 'quit'):break
a,b = int(cmd[1]),int(cmd[3])
if(a==b):continue
if(cmd[0]=='move'):clear(a)#先归位
if(cmd[2]=='onto'):clear(b)
pile(a,b)
printblock()
Blocks(10)


例题5-3:输入的文本,找出所有不同的单词,按从小到大输出:

这个题目也没有什么技巧,只是为了展示一下set的用法,注意python里面的set并不能自动排序,所以必须要用sort,但是sort又只有list有,所以还要转换成list。

def Andy():
while(True):
x = input()
if(x==''):return
s = [each if each.isalpha() else ' ' for each in x]
s = ''.join(s)
s = set(s.split())
s = list(s)
s.sort()
print(s)
for each in s:print(each)
Andy()


例题5-5本题具有一定的难度,其实这里用python还没有C++方便…我一开始也没有太好的思路,最后参考了书上的办法。由于这个题目是集合的集合,我们直接去模拟就会比较麻烦,因为题目的输出要求只是栈顶元素中集合的个数,所以我们只需要知道怎样区分两个不同的集合即可。而不需要知道集合内部到底是什么东西。因此选择用整数来代表一个集合,而栈则存储整数。

现在的关键就是在进行合并和取交集的时候还要找到整数对应的集合,所以用两个变量来存储集合和整数的转换关系。由于整数只是用来区分的,它自身并没有意义,所以可以依次分配逐渐增大的连续自然数,这样也方便我们编程。

最后注意,python中的set不能用来进行hash,所以要把set转换成元组,同时还要对set进行排序,因为集合是无序的,但是在hash的时候(3,2,1)和(1,2,3)会被认为是不一样的集合,所以我们要进行排序(当然这里可以优化采用非比较排序).

IDCache = {}#Set->ID
SetCache = []#ID->Set:Set的集合
#用来获取集合对应的ID
def get_ID(s):
s = list(s)
s.sort()#进行排序,保证集合标准化
s = tuple(s)
if s not in IDCache:
SetCache.append(s)
IDCache[s] = len(SetCache) - 1#添加新的集合
return IDCache[s]
s = []
op = input()
while(1):
if(op[0] == 'P'):#空集{}入栈
s.append(get_ID(tuple()))
elif(op[0] == 'D'):
s.append(get_ID(s[-1]))
else:
x1 = SetCache[s.pop()]#获取出栈的两个集合
x2 = SetCache[s.pop()]
if(op[0]=='U'):
x3 = tuple(set(x1)|set(x2))#取并集之后再转变成tuple
elif(op[0]=='I'):
x3 = tuple(set(x1)&set(x2))
else:
x2 = set(x2)
x2.add(get_ID(x1))
x3 = tuple(x2)
s.append(get_ID(x3))
#print(s,SetCache)
print(len(SetCache[s[-1]]),SetCache[s[-1]])
op = input()
if(op=='END'):break


拓展:如果要知道集合的具体情况怎么办?方法如下:

这样就可以获得集合的具体内部结构。

#用来获取集合对应的ID
IDCache = {():0}#Set->ID
SetCache = [()]#ID->Set:Set的集合
ExprCache = {0:'{}'}#存储集合具体情况
def get_ID(s):
s = list(s)
s.sort()#进行排序,保证集合标准化
s = tuple(s)
if s not in IDCache:
SetCache.append(s)
IDCache[s] = len(SetCache) - 1#添加新的集合
#将id转化为集合的表达式
i = IDCache[s]
x = SetCache[IDCache[s]]
ExprCache[i] = '{ '
for each in x:
ExprCache[i] += (ExprCache[each]+',')
ExprCache[i] = ExprCache[i][:-1]
ExprCache[i] += ' }'
return IDCache[s]


总结:本题代表的思路非常的有意思和重要,可以注意到这种递归定义的结构(集合)是不太好处理的,但是通过使用整数转换的方式就可以极大的简化编程的复杂度

例题5-6:本题不难,但是是一个比较好的例子来说明如何适当的选择数据结构.显然将每一个队伍分开存储是很有效的,长队列只需要存储团对编号的队列即可。这样每次操作都是O(1),假如将长队列直接存储所有人,那么每次入队操作都是O(n)。

当然还可以让长队列直接存储队列,这也就变成了队列的队列。有兴趣的可以试一下。

from collections import deque
def simulation():
team = {301:3,302:3,303:3,101:1,102:1,103:1,201:1,203:1,202:2}
team_que = deque()#团队的队列
people_ques = [deque() for i in range(4)]#每个团队的具体队列
op = ''
while(True):
op = input()
if op == 'stop':break
elif op == 'dequeue':
t = team_que[-1]#获取团队号码
people_ques[t].pop()#出队一人
if not len(people_ques[t]):
team_que.pop()
elif op.startswith('e'):
x = int(op.split(' ')[-1])

t = team[x]#获取该成员的团队号码
if not len(people_ques[t]):team_que.append(t)#该团入队列
people_ques[t].append(x)
for each in team_que:
for p in people_ques[each]:
print(p)
simulation()


例题5-7:丑数求解。本题主要采用了生成法来进行计算,关键是要想到:丑数就是只有2,3,5这个几个质数因子,那么它就只可能通过前面的丑数乘上2,3,5来生成。

另外一处关键是怎么得到第1500个丑数?我们通过优先队列存储数据,将每次将当前最小的丑数拿出来生成新的丑数,直到获得第1500个丑数为止。注意同一个丑数有很多生成的方式,因此要注意这个丑数是不是已经生成过了,在这里可以用set进行检查。

注意set里面存放的丑数不是完全按顺序来的,即便排序也不行,因为可能地1700大的丑数放入时1500大的还没有放进去。

def ugly_number():
cof = [2,3,5]
min_heap = [1]
all_num = set([1])
size = 1
while(1):
min_num = heapq.heappop(min_heap)
if(size == 1500):return min_num
size += 1
for i in cof:
x = min_num*i
if x not in all_num:
heapq.heappush(min_heap,x)
all_num.add(x)
print(ugly_number())
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: