360公司2017年春招两道编程题

分金子:

A、B两伙马贼意外地在一片沙漠中发现了一处金矿,双方都想独占金矿,但各自的实力都不足以吞下对方,经过谈判后,双方同意用一个公平的方式来处理这片金矿。处理的规则如下:他们把整个金矿分成n段,由A、B开始轮流从最左端或最右端占据一段,直到分完为止。  

马贼A想提前知道他们能分到多少金子,因此请你帮忙计算他们最后各自拥有多少金子?(两伙马贼均会采取对己方有利的策略)

输入

测试数据包含多组输入数据。输入数据的第一行为一个正整数T(T<=20),表示测试数据的组数。然后是T组测试数据,每组测试数据的第一行包含一个整数n,下一行包含n个数(n <= 500 ),表示每段金矿的含金量,保证其数值大小不超过1000。

样例输入:

2 
6
4 7 2 9 5 2
10
140 649 340 982 105 86 56 610 340 879

分析:这个问题可以使用递归算法,第一步由A先开始占坑,由于他采用利己策略,即他会在最左端和最右端的两端中,选择最终能让自己获得更多金子的那一段,关于这一点可以用一个递推式来描述,第二步再由B选择剩下的金子中最左边或者最右边那个,由于金子总量是不变的,所以B获得更多的金子和A获得更少金子是等价的

Python实现代码

def gold(list,i):
    if not list:
        return 0
    i+=1
    if i%2:
        return max([list[0]+gold(list[1:],i),list[-1]+gold(list[:-1],i)])
    else:
        return min(gold(list[1:],i),gold(list[:-1],i))

leng=int(input())
for i in range(leng):
    number=input()
    list1=list(map(int,input().split()))
    totel=sum(list1)
    most_a=gold(list1,0)
    print('Case#%d:'%(i+1)+str(most_a)+' '+str(totel-most_a))

在上述代码中i为基数时由a占坑,i为偶数时由b占坑

运行效果 

2 
6
4 7 2 9 5 2
Case#1:18 11
10
140 649 340 982 105 86 56 610 340 879
Case#2:3206 981

剪气球串:

小明买了一些彩色的气球用绳子串在一条线上,想要装饰房间,每个气球都染上了一种颜色,每个气球的形状都是各不相同的。我们用1到9一共9个数字表示不同的颜色,如12345则表示一串5个颜色各不相同的气球串。但小明希望得到不出现重复颜色的气球串,那么现在小明需要将这个气球串剪成多个较短的气球串,小明一共有多少种剪法?如原气球串12345的一种是剪法是剪成12和345两个气球串。

注意每种剪法需满足最后的子串中气球颜色各不相同(如果满足该条件,允许不剪,即保留原串)。两种剪法不同当且仅当存在一个位置,在一种剪法里剪开了,而在另一种中没剪开。详见样例分析。

输入

输入n个整数a1,a2,a3...an,ai表示该气球串上第i个气球的颜色。对于任意i,有1≤ai≤9。

分析:这个问题相对要麻烦一点,但是也可以用递归的思路解决,为了考虑n个气球有多少种剪法,可以先考虑前n-1个气球,如果第n个气球的颜色在前n-1个气球中未出现,那么所有n个气球的剪法就等于前n-1个气球的两倍;如果第n个气球的颜色在前n-1个气球中出现了,那么依次捆绑第n个球与前n-1个球中的最后一个球,最后两个球....,直到要捆绑的球里面出现颜色相同的球为止,分别考虑这些情况下的剪法数并将它们相加即可

Python实现

def cut(lt):
    if len(lt)==1:
        return 1
    if lt[-1] not in lt[:-1]:
            return 2*cut(lt[:-1])
    else:
        sum=cut(lt[:-1])
        for j in range(1,len(lt)-1):
            if lt[-1-j] not in lt[-j:]:
                sum+=cut(lt[:-1-j])
            else:
                break
        return sum

list1=[]
series=input()
for i in range(len(series)):
    list1.append(int(series[i]))
print(cut(list1))

随便测试了一下

123213
22

经过笔算验证,结果是正确的

但是,上面的递归过程涉及大量的重复计算,当输入很大时,运行效率极低(甚至运行不出来),类似于动态规划的思想,可以用一个字典来存储每一次的结果,避免重复运算,改进如下

import time

def cut(lt):
    if str(lt) in state.keys():
        return state[str(lt)]
    if len(lt)==1:
        return 1
    if lt[-1] not in lt[:-1]:
        state[str(lt)]=2*cut(lt[:-1])
        return 2*cut(lt[:-1])
    else:
        sum=cut(lt[:-1])
        for j in range(1,len(lt)-1):
            if lt[-1-j] not in lt[-j:]:
                sum+=cut(lt[:-1-j])
            else:
                break
        state[str(lt)]=sum
        return sum

time_start=time.time()
list1=[]
state=dict()
series=input()
for i in range(len(series)):
    list1.append(int(series[i]))
time_end=time.time()
print(cut(list1))
print(time_end-time_start)

输入输出和运行时间:

1321487423749201029389243043210394398727930478182912734
2271836383165440
4.648808240890503

分金子的问题也可以用同样方法进行改进。

以上是本人对这两个问题的解析,其中会有一些不合理的地方,如果看到这篇博客的朋友有更好的思路,请不吝赐教,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值