算法竞赛入门第五章习题分析(部分)

46 篇文章 0 订阅

这个题目不难,关键是理解题意,注意到是每一个单词的左边界的都要对齐而且要尽量往左靠,一开始我看成每一行了,结果写了半天觉得不对劲。有一个细节要注意,在使用split()进行分割时,会将空字符串也放进去,我们需要进行判断,如果遇到空字符就跳过。另外就是对每一列的最长单词进行统计,这样我们才能够对齐单词。

def format_print():
    line = input()
    words = []
    notes = []
    cols = [0]*10
    max = 0
    while line!='':
        line = line.split(' ')
        kase = 0#判断每一列的情况
        words.append([])#动态的生成list
        for word in line:
            if word =='':continue
            words[-1].append(word)
            if len(word) > cols[kase]:cols[kase] = len(word);#更新每一列的最大值
            kase += 1
        line = input()
    for line in words:
        col = 0
        for word in line:
            print(word+' '*(cols[col]+1-len(word)),end = '')
            col += 1
        print('')
format_print()

习题5-2:简单,直接计算.需要注意的是要先将第一个元素a[0]保存下来,因为a[0]会变化,另外对数组进行编号加快计算速度(value函数);唯一不方便的就是按格式读取输入…python每次都是一行进行读取,还要分开处理。

def Ducci(a):
    n = len(a)
    first = a[0]
    for i in range(n):
            a[i] = a[i] - (a[i+1] if i!=n-1 else first)
            a[i] = abs(a[i])
def value(a):
    value = 0;base = 1
    for digit in a:
        value = value*10 +digit
        base *= 10
    return value
def test(array):
    Cache = set()
    n = len(array)
    for i in range(1000):
        Ducci(array)
        val = value(array)
        if val == 0:print('ZERO');break
        if val in Cache:print('LOOP');break
        Cache.add(val)
def process_input():
    T = int(input())
    while T:
        T -= 1
        N = int(input())
        data = input()
        data = data.split(' ')
        line = [int(i) for i in data if i.isdigit()]
        test(line)
process_input()

习题3:此题简单,直接利用deque模块进行模拟就ok:

from collections import deque
def Throwing(q):
        discard = q.popleft()
        if len(q)>=2:
            card = q.popleft()
            q.append(card)
        return discard
def process():
    n = int(input())
    while n:
        q = deque([i for i in range(1,n+1)])
        print('Discarding cards: ',end = '')
        while(len(q)!=1):
            discard = Throwing(q)
            print(discard,end = ' ')
        print('\nRemaining Card :',q[0])
        n = int(input())
process()

习题四:交换学生,直接模拟即可,唯一需要注意的是(a,b)与(b,a)的相对关系.只需要将一个进入记录即可。

def test(record):
    for each in record:
        if record[each] != 0:
            return False
    return True
def process_input():
    while 1:
        n = int(input())
        if n == 0:break
        record = {}
        while n:
            line = input()
            line = line.split(' ')
            pair = (int(line[0]),int(line[1]))
            if pair in record:
                record[pair] += 1
            elif (pair[1],pair[0]) in record:
                record[(pair[1],pair[0])] -= 1
            else:
                record[pair] = 1
            n -= 1
        if test(record):print('Yes')
        else:print('NO')
process_input()

习题5:复合词。此题简单,关键是把每一个词是不是复合词的判定想好,将一个词语不断的拆分,然后检测拆分的两部分是不是在字典里面就ok。

def test(record_dict,word):
    length = len(word)
    for i in range(length-1):#(0,i),(i+1,length-1)
        w1 = word[0:i+1]
        w2 = word[i+1:length]
        if w1 in record_dict and w2 in record_dict:
            return True
    return False
def process_input():
    word = input()
    record = set()
    while word!='':
        record.add(word)
        word = input()
    for each in record:
        if test(record,each):
            print(each)
process_input()

习题6:判断对称轴,有一点小的陷阱要注意,首先要想好怎么计算一个水平线上的点的中线,方法对这些点按照x坐标排序后分别计算首尾对应点的中线,比如( 0,n1),1,n2

必须每一对的中线都一样才存在这一行的中线。

必须每一行的中线都一样才存在整个图的中线。

from collections import namedtuple
def average(each):
    return sum(each)/len(each)
def compute(line):
    line.sort()
    i = 0;j = len(line)-1
    mid_col = None
    while i <= j:
        temp = average([line[i],line[j]])#计算两个端点的中线
        if mid_col:
            if mid_col != temp:return None;
        mid_col = temp
        i += 1;j -= 1
    return mid_col
def test(record):
    mid_col = None
    for each in record:
        line = record[each]
        length = len(line)
        temp = compute(line)
        if not temp:return False#中间的线不存
        if mid_col:
            if mid_col != temp:#不相等
                return False
        mid_col = temp
    return True
def process_input():
    T = int(input())
    while T:
        record = {}
        n = int(input())
        while n:
            p = input()
            p = p.split(' ')
            y,x = int(p[1]),int(p[0])
            if y in record:record[y].append(x)
            else:record[y] = [x]
            n -= 1
        print('YES' if test(record) else 'NO')
        T -= 1
process_input()

例题7:本题仍然是直接模拟,但是要注意将优先级排序后单独存储起来,这样可以每次以常数时间获得剩下的最大优先级。

from collections import namedtuple,deque
task = namedtuple('task',['priority','isyou'])

def compute(q,priority_order):
    time = 0
    def move(q):
        max_priority= priority_order[-1]
        x = q.popleft()
        if x.priority < max_priority:
            q.append(x)
            return None#没有打印
        priority_order.pop()#删除一个最大优先级
        return x#返回打印的东西
    while 1:
        x = move(q)
        if x :
            time += 1
            if x.isyou:
                return time
def process_input():
    T = int(input())
    while T:
        line1 = input()
        line2 = input()
        line1,line2 = line1.split(' '),line2.split(' ')
        n = int(line1[0]);position = int(line1[1])
        priority_order = []
        q = deque([])
        i = 0
        for each in line2:
            t = task(int(each),True if i ==  position else False )
            q.append(t)
            priority_order.append(t.priority)
            i += 1
        priority_order.sort()
        print(compute(q,priority_order))
        T -= 1
process_input()

习题9:寻找代码bug。

这道题难度还是有的,做了一个小时。首先要将数组的名字与索引进行区分,比如a[index]=>a,index;其次index可能是一个表达式比如b[123]所以要对index作递归的分解和计算。在递归的时候要注意几点,如果该表达式是一个常量,就直接返回它的值,如果是一个表达式,就需要将名字和索引分开,继续递归求解索引.在获得索引的返回值之后,则需要进行判断,这个索引是否越界?不越界的情况下a[index]是否是初始化过的。如果都合法就将a[index]返回,否则返回None。

另外,在通过递归的函数get_value获得了相应的值之后,我们还需要在外层的程序进行判断,比如a[3]没有初始化过,但是a[3] = 1是合法的,而a[1] = a[3]是不合法的,所以要分开对待合法与否的条件。

再次,要将定义数组语句和赋值语句区分开来分别处理!

def get_index(var):
    left = var.index('[')+1
    right = len(var) - var[::-1].index(']')-1
    return var[left:right]
def get_arrname(var):
    left = 0
    right = var.index('[')
    return var[left:right]
def process_input():
    def find_bug(initial_line=None):
        def validate(name,index):
            print(arr_cache)
            if index!=None and index >=0 and index < arr_cache[name][0]and index in arr_cache[name][1]:#index合法且不越界,且a[index]存在
                return True
            return None
        def get_value(var):#返回变量的值
            if '[' not in var:return int(var)#常量必然合法
            #下面可能要递归的考虑问题
            name = get_arrname(var)
            index_expr = (get_index(var))#获取index的表达形式:a[a[b[3]]]=>a[b[3]]
            index = get_value(index_expr)#获取真正的值
            if validate(name,index):#index合法且不越界,且arr[index]已经初始化
                return arr_cache[name][1][index]
            return None
        line_index = 1
        arr_cache = {}
        flag = False
        while 1:
            if initial_line:line = initial_line;initial_line = None
            else:line = input()
            #下面才是开始查找bug的代码
            if '=' in line:#赋值语句
                print('check the assign equation')
                line = line.split('=')
                name = get_arrname(line[0])
                index = get_value(get_index(line[0]))
                value = get_value(line[1])
                print(name,index,value)
                if not((index>=0 and index!=None and index <arr_cache[name][0]) and value != None):#两边的变量是不合法的(越界和未初始化)
                    print(line_index,'error');flag = True
                else:
                    arr_cache[name][1][index] = value
            else:#数组定义
                name = get_arrname(line)
                size_expr = (get_index(line))#获取size的表达式
                size = get_value(size_expr)
                if not size>=0:#如果size不合法(negative,or None)
                    print(line_index,'error');flag = True
                else:arr_cache[name] = (size,{});#保存初始化的情况,size表示大小,{}用来存储哪些初始化了
            line_index += 1
        if not flag:print(0,'no error')
    while 1:
        x = input()
        if x=='.':break
        else:find_bug(x)
process_input()

习题10,此题细节比较繁琐,还是考验编码能力的….主要思路就是定义一个基本的操作(寻找关键字所在的文章),然后将所得的结果进行逻辑操作,最后还要输出结果,因为要输出的、结果是行,而且是只要还有一个关键字就要输出。

另外还要注意大写的单词也算,同时要计算最后一篇文章不输出’—-‘。

def process_input():
    def query(cmd):
        def simple_find(keyword):
            ans = set()
            for each in articles:
                article = articles[each]
                for line_index,line in enumerate(article):
                    if keyword in line or str.capitalize(keyword) in line:
                        ans.add(each)
                        break
            return ans
        ans = [];keys = []
        if 'AND' in cmd:
            cmd = cmd.split(' AND ')
            key1,key2 = cmd[0],cmd[1]
            keys.append(key1);keys.append(key2)
            ans.append(simple_find(key1))
            ans.append(simple_find(key2))
            final_ans = ans[0] & ans[1]
        elif 'OR' in cmd:
            cmd = cmd.split(' OR ')
            key1,key2 = cmd[0],cmd[1]
            keys.append(key1);keys.append(key2)
            ans.append(simple_find(key1))
            ans.append(simple_find(key2))
            final_ans = ans[0] | ans[1]
        elif 'NOT' in cmd:
            key1  = cmd.split(' ')[1]
            keys.append(key1)
            ans.append(simple_find(key1))
            final_ans = set([each for each in articles if each not in ans[0]])
        else:#只查询关键字
            key1 = cmd
            keys.append(key1)
            final_ans = simple_find(key1)
        if len(final_ans)==0:print('Sorry, I found nothing')
        else:query_lines(keys,final_ans,last_line,whole = True if 'NOT' in cmd else False)

    def query_lines(keys,indexs,whole=False):
        def test(line):
            for key  in keys:
                if key in line or str.capitalize(key) in line :return True
            return False

        indexs = list(indexs)
        indexs.sort()
        max_index = indexs[-1]
        kase = 0
        for each in indexs:
            article = articles[each]
            for i,line in enumerate(article):
                if test(line) or whole:print(line)
            if each != max_index:
                print('-'*10)
            kase += 1
#############################################################
    n = int(input())
    articles = {}
    indexs = {}
    kase = 0
    while n:
        articles[kase] = []
        article = articles[kase]
        while 1:
            line = input()
            if line == '*'*10:break
            article.append(line)
        kase += 1
        n -= 1
    n = int(input())
    while n:
        command = input()
        query(command)
        print('='*10)#输出分界线
        n -= 1
process_input()

5.11:

思路很简单,注意输出细节。

def find_changes(olddic,newdic):
    def print_format(array,symbol):
        if array==[]:return
        array.append(-1)
        print(symbol,end='')
        i = 0
        while 1:
            print(array[i],end='')
            if array[i+1]==-1:break
            print(',',end='')
            i+=1
        print('')
    #compare the element of olddic with new
    adds = [];deletes = [];changes = []
    for (key,val) in olddic.items():
        if key in newdic:
            if val != newdic[key]:
                changes.append(key)
        else:
            deletes.append(key)
    for (key,val) in newdic.items():
        if key not in olddic:
            adds.append(key)
    if adds==[] and deletes==[] and changes==[]:print('No Changes');return
    print_format(adds,'+')
    print_format(deletes,'-')
    print_format(changes,'*')
find_changes({'x':1,'xyz':123456789123456789123456789},{'xyz':123456789123456789123456789,'x':1})
find_changes({'first':1,'second':2,'third':3},
{'third':3,'second':2})

14

这道题还是比较麻烦,关键就在于要考虑效率,如果不考虑效率其实很简单。但是采取优先队列就会提高不少编程的难度,因为你不能直接修改优先队列里面的内容,必须取一个删一个,这样就导致采取命令Cancel的时候我们不可能直接删掉对应的订单(这样就必须把整个序列递归遍历一遍),而是采取一个标记数组来解决。然后下面分几部分讨论:

(1) :关于BUY/SELL命令:其实这个也就是不断的从相应队列里面取出元素,然后判断是不是没有被删除过的且满足条件,如果被删除过就不放回。如果不满足条件就结束测试(不满足交易条件),否则就要开始进行交易,注意在交易的时候要不断的测试,直到剩下的元素不满足条件或者需求已经完成!最后还要注意:如果某一条订单的需求量==0,那么就不要将它放入队列!

(2) 关于输出结果,这个也有点麻烦,关键在于不能够直接去遍历这个队列,同时还不能修改这个队列,所以只能采取递归的方式来不停的取元素,直到取到第一个不是被删除的元素,注意还没结束,因为输出要求是所有价格一样的订单要合在一起输出,所以还需要继续递归,直到找到一个不同于最小价格的元素就返回。在返回的途中还要注意将那些没有被删除的元素压入队列,而被删除的元素则不重新入队列。最后还要处理两种特殊情况,这里我采用了一个默认参数来解决问题。

(3) 关于(2)的代码感觉开销不小而且比较麻烦,那么有其他部分来计算Quote的输出呢?我认为还可以利用一个dict将所有的不同价格都保存起来,同时还随时监测最小价格的变化,这样到时候就可以直接找到输出。但是这样做的代价就是会要多写不少代码去维护你的dict和最小记录,比如在某个订单被删除之后就要进行相应的更新,同时在进行交易的时候某一个订单完成了,那么也要进行相应的更新。。。当然这样的开销比起(2)来还是要小一些,因为递归遍历的程度太深栈的开销也是很大的。

(4) 最后有几个细节要注意,python的优先队列是升序的,所以要降序必须要取相反数…..

下面的代码使用的是(1)(2)的思路:另外(1)的两个部分很容易合并,当时图简单就直接复制了。

from queue import PriorityQueue
def top_function():
    n = int(input())+1
    command = [0]*n
    sell_pq,buy_pq = PriorityQueue(n),PriorityQueue(n)
    kase = 1
    def get_lists(pq,defaultargs):
        def sumof(x):
            nonlocal pq
            if not pq.empty():
                t = pq.get()
                if t[0]!=x[0]:
                    if command[t[1]]!=-1:pq.put(t)
                    return 0
                else:
                    if command[t[1]]!=-1:z =  t[2]+sumof(t);pq.put(t)
                    else:z = sumof(t)
                    return z
            return 0
        if not pq.empty():
            x = pq.get()
            if command[x[1]]!=-1:
                ans =  [x[0],x[1],x[2]+sumof(x)]
                pq.put(x)
                return ans
            else:
                return get_lists(pq,defaultargs)
        return defaultargs
    while kase < n:
        cmd = input()
        cmd = cmd.split(' ')
        if cmd[0][0]!='C':
            type = cmd[0];q = int(cmd[1]);p = int(cmd[2])
        else:type = cmd[0];index = int(cmd[1])
        #################################################
        if type == 'BUY':
            while not sell_pq.empty():
                each = sell_pq.get()
                if  command[each[1]] == -1:continue#该命令已经被删除
                if abs(each[0]) > p:sell_pq.put(each);break#最低的卖价不满足,放回,结束
                min_q = min(each[2],q)#找到供求关系的最小量
                print('trade',min_q,each[0])
                each[2]-=min_q;q-=min_q#更新订单的需求
                if each[2]!=0:sell_pq.put(each)#重新插入
                if q==0:break
            if q!=0:buy_pq.put([-p,kase,q])#将这个订单入队列
        elif type == 'SELL':
            while not buy_pq.empty():
                each = buy_pq.get()
                if command[each[1]] == -1:continue
                if abs(each[0]) < p:buy_pq.put(each);break
                min_q = min(each[2],q)
                print('trade',min_q,-each[0])
                each[2]-= min_q;q-=min_q
                if each[2]!=0:buy_pq.put(each)
                if q==0:break
            if q!=0:sell_pq.put([p,kase,q])
        else:
            command[index] = -1
        x =  get_lists(buy_pq,[0,0,0])
        msg1 = '%s %s'%(x[2],-x[0])
        x = get_lists(sell_pq,[999999,0,0])
        msg2 = '%s %s'%(x[2],x[0])
        print('quote',msg1,'-',msg2)
        kase+=1
top_function()

习题15:此题python肯定要超时,不过没关系,主要理解思路。这里要采取字典树,简单说就是树的每一层保存所有可能的元素,然后不停的向下查询。它的查询效率很高,与字典应该差不多,但是由于使用共同前缀,避免了大量不必要内存的消耗。用python创建这样一个数据结构很简单,代码如下:

class Trie:
    def __init__(self):
        self.data = {'id':None}
    def query(self,str):
        now = self.data 
        for ch in str:
             if ch not in now: return -1
             now = now[ch]
        return now['id']
    def insert(self,str,id):
        now = self.data
        for ch in str:  
            #标记路径,没有被访问过就要标记
            if  not now['id'] :now['id'] = id;  
            if ch not in now:
                now[ch] = {'id':None}#id 代表着对应的值,同时也可以标记是否访问过 
            now = now[ch]
        if not now['id']:
                now['id'] = id
t = Trie()
t.insert('1',1)
fib = [1,1]
record = {}
for i in range(2,50001):
    fib[0],fib[1] =fib[1],fib[0]+fib[1]
    s = str(fib[1])[0:40]
    t.insert(s,i)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值