算法竞赛入门经典第四章:部分习题解答

46 篇文章 0 订阅

跳过了部分习题。。。

习题4-2:此题不难,我采取最直接的办法,暴力遍历进行判断,代码如下:
def do_squares(h,v,n):#h,v是一个矩阵
    def test(i,j,l):
        _i = i;_j = j
        while(_j<j+l):
            if(h[_i][_j]==0):return False
            _j+=1
        while(_i<i+l):
            if(v[_i][_j]==0):return False
            _i+=1
        while(_j>j):
            if(h[_i][_j-1]==0):return False
            _j-=1
        while(_i>i):
            if(v[_i-1][_j]==0):return False
            _i-=1
        return True
    count = [0]*(n-1)
    for i in range(1,n-1):
        for j in range(1,n-1):
            for length in range(1,n-1):#长度从1-n-1
                if(test(i,j,length)):count[length]+=1
    for k in range(1,n-1):
        if(count[k]!=0):print('Number of suqare with length(%d) : %d'%(k,count[k]))
def squares(h,v,n):
    H = [ [0]*(n+1) for i in range(n+1)]
    V = [ [0]*(n+1) for j in range(n+1)]
    for x in h:H[x[0]][x[1]] = 1
    for y in v:V[y[0]][y[1]] = 1
    do_squares(H,V,n+1)
squares([(1,1),(1,3),(2,1),(2,2),(2,3),(3,2),(4,2),(4,3)]
        ,[(1,1),(1,2),(2,2),(3,2),(2,3),(1,4),(2,4),(3,4)],4)

算法复杂度是O(n^4),有没有办法降低一下复杂度呢?仔细思考一下,可以构造一个数组用来存储点之间的互联情况,先从边长最大的正方形开始检验,这样可以把能够组成一条边的点信息记录下来,先检验是否已经连通,如果没有再采用遍历检验的办法.这样能避免很多多余的检验,但是增加了空间复杂度.

习题4-4:用python解决这个问题确实简单…..排序后比较就ok:

def Cube(a,b):
    X = [[a[0],a[5]],[a[1],a[4]],[a[2],a[3]]]
    Y = [[b[0],b[5]],[b[1],b[4]],[b[2],b[3]]]
    for i in range(3):
        X[i].sort();Y[i].sort()
    X.sort();Y.sort()
    print(X==Y)
Cube('grggrb','rggbgr')

习题4-5:IP地址求最大子网和子网掩码,没啥好说的,直接求解.

def convert(x):
    ans = ''
    tmp = x.split('.')
    for each in tmp:
        tmp_ans = bin(int(each))[2:]
        ans += '0'*(8-len(tmp_ans))+tmp_ans
    return ans
def recovert(y):
    ans = ''
    for i in range(0,32,8):
        ans += str(int(y[i:i+8],base = 2))+'.'
    return ans[:-1]
def solve(IP):
    net = convert(IP[0])
    for each_ip in IP[1:]:
        tmp = convert(each_ip)
        for i in range(len(net)):
            if(tmp[i]!=net[i]):net = net[:i];break
    mask = '1'*len(net)+'0'*(32-len(net))
    net += '0'*(32-len(net))
    print('%s*********************%s'%(recovert(net),recovert(mask)))
solve(['194.85.160.183','194.85.160.177','194.85.160.178'])

习题4-7:RAID技术,用校验码来恢复数据.本题也略烦。基本思路就是在几个进制之间不停的转换.主要是熟悉hex,int,bin几个方法

def do_raid(d,s,b,Type):#b代表数据块
    check_result = 0 if Type=='E' else 1
    def eor(x,check_pos):
        ans = 0
        count = 0
        err_pos = 0
        for i,each in enumerate(x):
            if(each!='?'):ans ^= int(each,base = 2)
            else:count+=1;err_pos = i
        if(count>1):return False,x
        elif(count==1):
            tmp = bin(ans^check_result)[2:]
            x[err_pos] = '0'*(s-len(tmp))+tmp
        return True,x[0:check_pos]+x[check_pos+1:]#去掉校验码的部分
    check_pos = 0
    final_ans = ''
    for i in range(0,len(b),d):
        if(b[check_pos]=='?'):print('check illegal');return
        test,tmp = eor(b[i:i+d],check_pos%d)
        if not test:print('too many error');return#磁盘数据损坏过多
        check_pos += (d+1)
        print(tmp)
        final_ans += ''.join(tmp)
    print(final_ans,hex(int(final_ans,2)))
do_raid(5,2,['00','01','10','11','?','01','10','11','10','?','01','11','01','10','?',
'11','10','?','11','01','11','11','11','00','11'],'E')

习题4-8,此题略有一点难度,主要注意3点,第一:在时间临界点的时候认为它是属于前面时间区域的,第二,在超过时间周期之后应该取余取回来,但是这样存在一个问题,取余之后得到的是0,但是实际上该是1,因为实际意义上从末端走了1格应该是走到1而不是0,当然还可以为了避免讨论边界情况我们统一把初始时间向前推0.5min(这是我的做法).最后注意什么情况下是没有解的?我采取的办法是当时刻表和最开始的时刻重复且此时没有全部清醒时返回-1.

def Tired_students(students):
    n = len(students)
    A = [students[i][0] for i in range(n)]
    B =  [students[i][1] for i in range(n)]
    C =  [students[i][2]-0.5 for i in range(n)]
    print(A,B,C)
    time = 1
    sleeped = 0
    initial_c = C[:]
    tmp = [1]*n
    for i in range(n):
        if(C[i]>A[i]):sleeped += 1;tmp[i] = 0 
    print(sleeped,tmp)
    while(sleeped !=0):
        time += 1
        tmp_sleeped = 0
        for i,each in enumerate(C):#遍历每一个学生的情况
            C[i] = (C[i]+1)%(A[i]+B[i])#获取当前在周期里面的位置
            if(C[i]>A[i]):#到了睡觉时间
                if(tmp[i] == 1):#上一刻处于听课
                    if(2*sleeped > n):#满足睡觉条件
                        tmp[i] = 0
                        tmp_sleeped += 1
                    else:
                        C[i] = 0#在睡觉A[i]分钟
            elif(tmp[i]==0):#到了醒来的时间#上一刻处于睡觉状态
                    tmp[i] = 1
                    tmp_sleeped -= 1
        sleeped += tmp_sleeped
        print('the %d分钟: '%time,tmp,sleeped,C)#测试情况
        if(initial_c == C and sleeped != 0):return -1#不会有这个时刻
    return time

习题4-10:给定一个m*n的网格,每个网格都有一定的高度,里面有一定体积的海水,网格周围是无限高的墙,求解海水的海拔.一种思路如下:首先对海拔进行排序,然后每次尝试一个新的海拔高度,如果发现海水的体积加上网格的体积小于整个长方体(网格)的体积就退出循环.否则继续试探。

然后假设在上一个临界高度之上还应该多出h’的高度,列出方程:v0+v1+h’(num - i) = 100*num(h+last_h),计算出last_h+h’就得到最后的高度.
def Flooded(m,n,h,v):
    h.sort()#对高度排序
    num = m*n
    v0 = v
    v1 = 0
    tmp_h = 0
    final_h = 0
    count = 0
    last_h = 0
    i = 0
    while(i<100):
        if(h[i] != last_h):
            if(v0+v1+(num - i)*(h[i]-last_h)*100 < h[i]*num*100 ):
                final_h = last_h;break
            else:
                v1 += (num - i)*(h[i]-last_h)*100
                last_h = h[i]
        i += 1
    x = num - i
        print(final_h,(v0+v1-100*num*last_h)/(100*num-x*num)+last_h,i/num)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值