2020.4.8 阿里巴巴笔试

题目1

小强开始了他的功夫特训,特训的时间为n秒,有m个木头人,每个木头人的血量为a。小强的攻击范围为b,小强每次攻击能对最多b个存活的木头人造成1点伤害,每次攻击需要1秒,当经过一次攻击后木头人的血量为0,木头人死亡。当特训结束时,小强最多能消灭多少个木头人。
输入:
T
n,m,a,b
例:
input:
1
5 5 2 2
output:
5
解释:
第一次攻击:1 1 2 2 2
第二次攻击:1 0 1 2 2
第三次攻击:0 0 0 2 2
第四次攻击:0 0 0 1 1
第五次攻击:0 0 0 0 0

分析题目(菜鸡的分析过程)

1、第二次攻击是很关键的一次选择,如果第二次攻击结果为:0 0 2 2 2,那么小强最多能消灭4个木头人,这显然不是最优选择。但是这个问题似乎将思路引入贪心:优先选择血量较多的木头人攻击。但是这种贪心的思路显然不对,事实是有时需要优先攻击血量较多的木头人,有时需要优先攻击血量较少的木头人。那么在什么样的条件下进行不同的选择呢?。。。太复杂了涉及攻击范围,小兵血量,剩余攻击次数多个条件,放弃。
2、把问题抽象成,对于当前的一个小兵,是否选择其进行攻击。问题似乎转化成动态规划。如果攻击当前小兵,则血量减一;否则继续选择下一个小兵。打表的话似乎需要一个a*b*m*m的空间存储所有可能性,问题将变得越来越复杂。是否可以使用递归进行动态规划呢?问题依然很复杂,除了动态规划打表对应的m*m一层递归外(一波攻击的最优选择),还有攻击范围b对应的第二层递归(一次攻击的最优选择),问题更复杂了。打表和递归的方法都进行了尝试,均以失败告终。
3、无论是贪心或者动态规划,都是想要模拟小强攻击的过程,即如果采用贪心或动态规划的解法,不仅可以得到最多可以消灭多少个木头人,还可以得到攻击的方案。即如果题目修改为需要进攻方案(可能有多种答案),则只能选择贪心或动态规划求解。
4、然而,题目只需要最多可以消灭多少个木头人,并不需要进攻方案。min(n*b // a, m)似乎就可以得到结果,n*b // a是最优进攻方案最多可以消灭的木头人数量。但是攻击是有限制的,每次只能选择攻击范围内的b个木头人减去一滴血,如果木头人的血量大于攻击次数,那么一个木头人都不能消灭。那么问题就可以按照如下思路求解:1、如果n < a,则返回0,否则返回min(n、*b // a, m)。(不确定正确,欢迎留言质疑)

if __name__ == "__main__":
    # 读取第一行的n
    num = int(sys.stdin.readline().strip())
    ans = []
    for i in range(num):
        # 读取每一行
        line = sys.stdin.readline().strip()
        # 把每一行的数字分隔后转化成int列表
        values = list(map(int, line.split()))
        n, m, a, b = values
        if n < a:
            die = 0
        else:
            die = min(n*b // a, m)
        print(die)

题目二

有一个方格游戏是这样的:给一个n*n的矩阵,矩阵每一个位置都有一个值,初始的时候在左上角出发,并获得该位置的值作为初始能量,
每次可以选择向上、向下、向左、向右四个方向中的一个,并且可以移动距离不大于K,且要保证到达的格子的值要比当前位置的格子的值要大,
否则不能移动到这个格子,当无法移动时,游戏结束。
现在想知道在所有可能的走的方案中,到达的位置的格子值相加总和最大为多少。
输入第一行一个整数T,代表有T组测试数据。
接下来先输入两个整数n和k,代表矩阵的大小以及每一步最多能走多少个格子。
接下来n行,每行n个数,代表矩阵的A的元素。
例如:
输入:
1
3 1
0 5 37
55 10 19
12 9 18
输出:
71
解释:
最大路径为:0——5——10——19——37。和为71。

分析题目(菜鸡的分析过程)

1、走矩阵,这种问题显然需要动态规划打表解决,或者广度优先遍历也可以解决。这里采用动态规划方案。对于动态规划问题,首先需要定义dp数组和dp数组元素对应的意义,这里定义dp[n][n],dp[i][j]表示从左上角走到i,j位置的最优策略。
2、对于这道题,要求到达的格子的值要比当前位置的格子的值要大,这个条件保证了行走过程中不会走回头路,即一直朝着最大的目标前进,且走过的格子不会回头再走。这是一个很重要的条件,如果没有这个条件限制,打表过程中还需要记录走过的路径,防止回头,但变换后的问题仍然可以求解。
3、动态规划问题,最重要的就是递归表达式:
当前位置(i,j),下一步位置(k,l),(k,l)在(i,j)所能走到的下一步范围。
d p [ k ] [ l ] = m a x ( d p [ k ] [ l ] , d p [ i ] [ j ] + a r r [ k ] [ l ] ) , dp[k][l] = max(dp[k][l], dp[i][j] + arr[k][l]), dp[k][l]=max(dp[k][l],dp[i][j]+arr[k][l])
cond. (i,j)位置可达,(i,j)可达(k,l)
为了标注不可抵达的格子,我们将dp初始化为-1;dp[0][0]=arr[0][0]。
4、上面提到,需要确定攻击范围。“每次可以选择向上、向下、向左、向右四个方向中的一个,并且可以移动距离不大于K”,则对应当前位置(i,j)为中心的十字形区域。

# dp[i][j]表示走到i,j位置的最优策略
# dp[i][j]等于0表示绝路

# i,j走到k,l
def find_max_path_v1(arr, n, K_):
    dp = []
    for i in range(n):
        dp.append([-1] * n)
    dp[0][0] = arr[0][0]
    max_sum = -1
    for i in range(0, n):
        for j in range(0, n):
            # 不能走斜线
            for k in range(max(i-K_, 0), min(i+K_+1, n)):
                l=j
                if arr[k][l] > arr[i][j] and dp[i][j] > -1:
                    dp[k][l] = max(dp[k][l], dp[i][j] + arr[k][l])
                    if dp[k][l] > max_sum:
                        max_sum = dp[k][l]
            for l in range(max(j-K_, 0), min(j+K_+1, n)):
                k=i
                if arr[k][l] > arr[i][j] and dp[i][j] > -1:
                    dp[k][l] = max(dp[k][l], dp[i][j] + arr[k][l])
                    if dp[k][l] > max_sum:
                        max_sum = dp[k][l]
    print(dp)
    return max_sum

if __name__ == "__main__":
    # 读取第一行的n
    num = int(sys.stdin.readline().strip())
    ans = []
    for i in range(num):
        # 读取每一行
        line = sys.stdin.readline().strip()
        # 把每一行的数字分隔后转化成int列表
        values = list(map(int, line.split()))
        n, k = values
        arr = []
        for j in range(n):
            line = sys.stdin.readline().strip()
            arr.append(list(map(int, line.split())))
        res = find_max_path_v1(arr, n, k)
        print(res)

以上两个题目都没有经过AC测试,不确定正确,欢迎留言质疑。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值