算法竞赛入门经典第三章总结(python实现)

57 篇文章 1 订阅
48 篇文章 0 订阅
#开灯问题
def N_lamps(n,k):
    L = [0]*(n+1)
    for i in range(1,k+1):
        factor = 1
        while(factor*i<=n):
            L[factor*i] = 1-L[factor*i]#1代表开
            factor += 1
    for k,i in enumerate (L):
        if(i==1):print(k,' ',end = '')
         ###没啥好说的,简单的模拟 
 #蛇形矩阵
def snake_matrix(n):
    L = [[0]*n for i in range(n)]
    dir = [(1,0),(0,-1),(-1,0),(0,1)]
    i = 0;j = n-1;x = 1;
    L[i][j] = 1;#先初始化第一个位置
    while(x<n*n):
        while(i<n-1 and (L[i+1][j]==0)):
            L[i+1][j] = x+1;x+=1;i+=1
        while(j>0 and (L[i][j-1]==0)):
            L[i][j-1] = x+1;x+=1;j-=1
        while(i>0 and (L[i-1][j]==0)):
            L[i-1][j] = x+1;x+=1;i-=1
        while(j<n-1 and  (L[i][j+1]==0)):
            L[i][j+1] = x+1;x+=1;j+=1
    for row in L:
        print(row)
###本题有两种思路,但是一个是跟着填入的数字走,根据当前坐标判断下一个数字应填在哪里,另一种思路就是跟着轨迹走,下,左,上,右不断循环.第一种方法相对麻烦(我就采取的第一种~~~~),因此给出了第二种的python实现.

 ###竖式问题,给定一个数字集合,找出所有形如abc*de的竖式算式,并且保证完整的竖式所有数字都是属于该集合。输出所有的竖式和解的行数。代码如下:
number = 0
def vertical_form_de(abc,data):
    def test(buf,data):
        for each in buf:
            if(int(each) not in data):return False
        return True
    global number
    for d in data:
        for e in data:
            de = d*10+e
            x = abc*d;y = abc*e;z = abc*de
            buf = str(x)+str(y)+str(z)
            if(test(buf,data)):
                number+=1;
                print('%5d\nX%4d\n-------\n%5d\n%4d\n------\n%5d\n'%(abc,de,x,y,z))

def  vertical_form_abc(data):
    for a in data:
        for b in data:
            for c in data:
                vertical_form_de(a*100+b*10+c,data)
    print('The number of solutions = %d'%number)

###书上给出的解法太暴力了,我的思路是从给定的集合里面来遍历.需要注意下输出格式,利用%5d,%4d来控制格式.

def toTex(buf):
    left_right = 0#0代表左,1代表右
    new_buf = ''
    for each in buf:
        if(each == '"'):
            if(left_right == 0):each = '“'
            else: each = '"'
            left_right = 1 - left_right
        new_buf+=each
    print(new_buf)
#toTex('" To be or bot to be," quoth the Brad,"that is the question".')
#字符错位问题
####这题也很简单,关键是用好一个数组,一开始我想用map来解,但是这有些自找麻烦。。。。还是这个策略更容易实现.这也提醒我们使用map前要考虑有没有更简单的对应关系.


def WERTYU(buf):
    s = '`1234567890-=QWERTYUIOP[]\\ASDFGHJKL;ZXCVBNM,./'
    for char in buf:
        try:
            pos = s.index(char)
        except:
            pos = -1
        if(pos!=-1):print(s[pos-1],end = '')
        else:print(char,end = '')
#WERTYU('O S, GOMR YPFSU/')
def palindromes(buf):
    s = 'A   3  HIL JM O   2TUVWXY51SE Z  8 '
    msg = ['is not a pal','is a regular pal','is a mirrored string','is a mirrored pal']
    def rev(ch):
        if(ch.isalpha()):return s[ord(ch)-ord('A')]
        else :return s[ord(ch)-ord('0')+25]
    print(buf,buf[::-1])
    p = 1 if (buf[::-1]==buf) else 0#回文
    old_buf = list(buf)
    buf = list(map(rev,buf))
    m = 1 if (buf[::-1]==old_buf) else 0
    print(m,p,buf,old_buf)
    print (msg[2*m+p])
#palindromes('NOTAPALINDROME')
#palindromes('2A3MEAS')
#palindromes('ATOYOTA')
###方法是采取一个数组来进行映射,注意几点:首先reverse会改变自己所绑定的值,因此较好的办法是采取[::-1]的切片,对于str和list都是通用的.另外四种情况的组合最后是采取一个类似二进制的表达来输出的.msg的作用很有意思.


def master_mind(ans,guess):
#test从guess本身考虑问题
    def test(each):
        tot = 0
        wrong_occur = 0
        mark = [0]*len(each)
        for index in range(len(each)):
            if(ans[index] == each[index]):tot+=1;mark[index]=1;
        for index in range(0,len(each)):
            if(mark[index]!=1):
                pos = -1
                while(True):
                    try:
                        pos = ans.index(each[index],pos+1)
                        if(mark[pos]!=1):wrong_occur += 1;break;
                    except:break
        return tot,wrong_occur
#test1从1-9数字的角度考虑问题
    def test1(each):
        A = 0;B = 0;
        for index in range(len(each)):
            if(ans[index] == each[index]):A+=1;
        for d in range(1,10):
            c1 = 0;c2 = 0;
            for index in range(len(each)):
                if(ans[index]==d):c1+=1;
                if(each[index]==d):c2+=1;
            B += min(c1,c2);
        return (A,B-A)
    for each in guess:
        print(test(each))
###其实这个问题书本上的解法很有意思,值得思考,我给出的解法需要多用一个数组作为标志数组,算法复杂度是O(n^2),首先求解出A并且做好标记,然后对guess序列的每一个数进行在ans中的查找,直到有一个位置的数字被找到且没有被标记过.
 ###而书本的答案则只需要O(n*m)的复杂度,其中m是代表可能出现的数字集合的长度.
 ###书本的答案采取的办法是统计每一个数字在ans和guess序列出现的次数,然后取两者中的最小值,这代表它们对B做出的贡献,然后不断累加即可.最后注意要减去A,因为不能计算已经正确包含的值.




def mysum(x):
    sum = x
    while(x>0):
        sum += x%10
        x //= 10
    return sum
def min_generator(n):
    m = len(str(n))#获取n的位数
    for x in range(n-9*m,n):#n-9m -> n-1
        if(mysum(x)==n):print(x);return
    print(0)
#min_generator(121)
#mysum(5555)
def less(a,b):
    a_l = len(a);b_l = len(b)
    if(a_l > b_l): return less(b,a)
    n = a_l if a_l < b_l else b_l
    for i in range(n):
        if(a[i]<b[i]):return True#小于
        elif(a[i]>b[i]):return False#大于
    if(b_l == a_l):return False#相等
    else: return True#小于

###这个题目不难,但是书上给出的答案有点意思,居然是空间换时间的做法,这个角度值得思考.但是还是有其他办法的,可以很容易的缩小枚举的范围是n-9m到n,m是n的位数.

def circular_s(s):
    temp = s
    for i in range(len(s)):
        if(less(temp,s)):ans = temp
        temp = temp[1:]+temp[0]
    print(ans)
#circular_s('CGAGTCAGCT')


def score(buf):#统计连续出现的O
    curr_num = 0;tot = 0
    for ch in buf:
        if(ch=='O'):curr_num+=1;
        else: curr_num = 0
        tot += curr_num
    print(tot)
#score('OOXXOXXOOO')
def Molar_Mass(buf):
    weight = {'C':12.01,'H':1.008,'O':16.00,'N':14.01}
    sum = 0.0;i = 0
    length = len(buf)
    while(i+1<length):
        if (buf[i+1].isdigit()):sum += weight[buf[i]]*int(buf[i+1]);i+=2
        else: sum += weight[buf[i]];i+=1;
    if(i==length-1):sum+= weight[buf[i]]
    print(sum)
#Molar_Mass('C6H5OH2')
###唯一需要注意的是最后一个分子
def get_zero(i,num):
    tot = 0
    u = 1;
    for j in range(1,i+1):
        x = j*u;
        tot += x
        u *= 10;
    tot*=9
    return  tot - num*9
def digit_count(n):
    k = 0;i = 0;
    while(True):
        if((k*10+9) <= n):k = k*10+9;i+=1;
        else:break
    gap = n - k
    sum = [i*(k+1)//10]*10;
    sum[0] = get_zero(i,sum[1])
    for digit in range(k+1,n+1):
        for ch in str(digit):
            sum[ord(ch)-ord('0')]+=1
    print(sum)
#digit_count(5000)
####这个题目暴力解就太简单了,这里稍微用了一点数学的知识:
 ####我们首先看一看1-999的出现情况,首先假设数字出现的规律是001-999,这样因为每一位都是平均分配的,所以每一位出现的次数是3*1000/10
 = 300
def perodic(buf):
    def test(s):
        k = len(s)
        for i in range(len(buf)//k):
            if(s!=buf[i*k:(i+1)*k]): return False
        return True
    beg = '';end = ''
    length = len(buf)
    for i in range(length//2 + 1):
        beg += buf[i]
        end = buf[length-i-1]+end
        if(beg == end and test(beg)):print(len(beg));return
    print(len(buf))
#perodic('abccbaabccxba')
 ###这里的算法复杂度是n^2,先从两头开始比较,最坏情况下比较的次数是1+2+3+....(n/2) = n^2/8,然后进行检验,检验的比较次数是n.
 ###如果采取最直接的暴力比较法,最坏情况下的比较次数是(n/2)*n = n^2/2,显然上面的算法要好一些.但是还是不能降低复杂度的量级.... 


#空格迷题
def get_pos(matrix):
        for i,k in enumerate(matrix):
            for j,item in enumerate(k):
                if(item == ' '):return[i,j]
def Puzzle(matrix,command):
    empty_pos = get_pos(matrix)
    dir = {'A':[-1,0],'B':[1,0],'L':[0,-1],'R':[0,1]}
    def move(instruction):
        nonlocal empty_pos#声明它不是move的局部变量
        old_i = empty_pos[0]
        old_j = empty_pos[1]
        if(instruction not in dir):return False
        i = old_i + dir[instruction][0]
        j = old_j + dir[instruction][1]
        if(i<5 and i >0 and j<5 and j>0 ):
            matrix[old_i][old_j],matrix[i][j] = matrix[i][j],matrix[old_i][old_j]#交换两个元素
            empty_pos = [i,j]
            return True
        else:return False
    for ins in command:
        if(not move(ins)):print('This puzzle has no final configuration');return
    for i in matrix:
        print(i)
A = [['T','R','G','S','J'],['X','D','O','K','I'],['M',' ','V','L','N'],['W','P','A','B','E'],['U','Q','H','C','F']]
Puzzle(A,'ARR')

简单的模拟而已,需要注意的是python3 nonlocal的用法可以用来修改外部的自由变量.

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值