【编程题】网易游戏社招编程题题解

网易游戏社招编程题题解

  • 题目1:地形最低值的和
  • 题目2:最少花的钱数
  • 题目3:粉刷墙壁
  • 题目4:被n到达的数

题目1:地形最低值的和

题目描述:在一个 n × m n \times m n×m的地图中,每一个格子都有一个数字代表该地形的高度,地图的行数从1到n,列数从1到m。
地形的高度由一个递推式子给出,该递推式为:
g ( i ) = ( g ( i − 1 ) ∗ x + y ) m o d z g(i)=(g(i - 1) * x + y) mod z g(i)=(g(i1)x+y)modz
第i行,第j列的地形块的高度为h(i,j),其值为:
h ( i , j ) = g [ ( i − 1 ) ∗ m + j − 1 ] h(i,j)=g[(i - 1)*m+j-1] h(i,j)=g[(i1)m+j1]
对于每一个长度为a*b的子地形块,你都需要计算出其中最低的地形的高度值,并输出这些最低的值的和。
输入描述:
第一行四个整数,分别为n,m,a,b。(1<=n,m<=3000,1<=a<=n,1<=b<=m)
第二行四个整数,分别是g(0),x,y,z。(0<=g(0),x,y,z<=10^9)
输出描述:
一个整数,所有地形最低值的和。

示例:
输入:
3 4 2 3
1 2 3 59

输出:
15

说明:
地形块为:
1 5 13 29
2 7 17 37
18 39 22 47
需要计算的子地形块有4个:
1 5 13
2 7 17

5 13 29
7 17 37

2 7 17
18 38 22

7 17 37
39 22 47

最低的地形块之和为1+5+2+7=15

解题思路:
第一步:计算每个地形的高度;第二步,根据地形高度构建地形块;第三步,遍历所有子地形块,计算最低的地形块之和。
注意,z不能为0
完整代码:

import numpy as np
import random

def g_list(g0, gLens, x, y, z):
    g_list = []
    g_list.append(g0)
    for i in range(1, gLens):
        if z is 0:
             g_i = 0
        else:
            g_i = (g_list[i - 1] * x + y) % z
        g_list.append(g_i)
    return g_list

def h_matrix(n, m, g_values):
    matrix = []
    for i in range(1, n+1):
        mat = []
        for j in range(1, m+1):
            k = (i - 1) * m + j - 1
            mat.append(g_values[k])
        matrix.append(mat)
    return matrix

def sum_lower(n, m, a, b, g_0, x, y, z):
    g_lens = n * m
    g_values = g_list(g_0, g_lens, x, y, z)
    matrix = np.asmatrix(h_matrix(n, m, g_values))
    res = 0
    #print("地形块矩阵为:")
    #print(matrix)
    for r_start in range(n-a+1):
        r_end = r_start + a
        #print("起始行号:{}, 结束行号:{}".format(r_start, r_end))
        for c_start in range(m-b+1):
            c_end = c_start + b
            #print("    起始列号:{}, 结束列号:{}".format(c_start, c_end))
            tmp = matrix[r_start:r_end, c_start:c_end]
            res += np.min(tmp)
            #print("    分片矩阵为:")
            #print(tmp)
            #print("    该分片中最低值为:{}".format(np.min(tmp)))
    return res

def build_random():
    for i in range(3):
        n,m = [random.randint(1,10) for _ in range(2)]
        a = random.randint(1,n)
        b = random.randint(1,m)
        g_0,x,y,z = [random.randint(0,10^9) for _ in range(4)]
        print("第{}次模拟...".format(i))
        print("n m a n :{} {} {} {}".format(n,m,a,b))
        print("g_0 x y z :{} {} {} {}".format(g_0,x,y,z))
        res = sum_lower(n, m, a, b, g_0, x, y, z)
        print("最小值为:{}".format(res))

if __name__ == '__main__':
    n,m,a,b = map(int,input().split(" "))
    g_0,x,y,z = map(int,input().split(" "))
    res = sum_lower(n, m, a, b, g_0, x, y, z)
    print("最低的地形块之和为{}".format(res))
#     build_random()

题目2:最少花的钱数

题目描述:商店中有n件商品,每件商品的价格为ai。该商店为了回馈客户,发布了m种优惠,顾客在一次付款的时候可以选择一种优惠。每种优惠给出两个值x和y,其表示,如果在该次消费中购买了x及以上件商品,则这些商品中最便宜的y件商品免费。
小林想知道,把n件商品全部买完,最少需要花多少钱。(小林可分多次购买)
输入描述:
第一行两个数n和m,(1<=n<=2000,1<=m<=10^5)
第二行n个数:a1,a2,a3,…,an,表示各个商品的价格。(1<=ai<=100000)
接下来的m行,每行两个数x和y,表示该类优惠的打折策略。(1<=x<=n,0<=y<=x)
输出描述
一行一个整数,表示最少需要花的钱数。
示例:
输入:
7 4
2 5 4 2 6 3 1
2 1
3 2
5 3
3 1
输出:
10

说明:
第一次买商品7,不使用优惠,花1元。
第二次买商品1,商品4和商品6,使用2号优惠,花3元。
第三次买剩下的商品,使用2号优惠,花6元。
一共消费10元

解题思路:
本题中,每种类型商品买一件,把n件商品全部买完,计算最少需要花的钱数。以示例中的数据为例,理论上,最多可以购买7次,最少可以购买1次;另外,每次的购买数量最多7个,最少1个,所以由此可知:
第一次购买:可以采取的策略有:买1件、买2件、买3件、买4件、买5件、买6件、买7件
如果第一次购买了1件商品,则第二次购买可以采取的策略有:买1件、买2件、买3件、买4件、买5件、买6件
如果第一次购买了2件商品,则第二次购买可以采取的策略有:买1件、买2件、买3件、买4件、买5件
如果第一次购买了3件商品,则第二次购买可以采取的策略有:买1件、买2件、买3件、买4件
如果第一次购买了4件商品,则第二次购买可以采取的策略有:买1件、买2件、买3件
如果第一次购买了5件商品,则第二次购买可以采取的策略有:买1件、买2件
如果第一次购买了6件商品,则第二次购买可以采取的策略有:买1件

如果第一次购买了1件商品,第二次购买了1件商品,则第三次购买可以采取的策略有:买1件、买2件、买3件、买4件、买5件
如果第一次购买了1件商品,第二次购买了2件商品,则第三次购买可以采取的策略:买1件、买2件、买3件、买4件
如果第一次购买了1件商品,第二次购买了3件商品,则第三次购买可以采取的策略:买1件、买2件、买3件
如果第一次购买了1件商品,第二次购买了4件商品,则第三次购买可以采取的策略:买1件、买2件
如果第一次购买了1件商品,第二次购买了5件商品,则第三次购买可以采取的策略:买1件
如果第一次购买了1件商品,第二次购买了6件商品,则购买结束
…(以此类推)
如果第一次购买了1件商品,第二次购买了1件商品,第三次购买了1件商品,第四次购买了1件商品,第五次购买了1件商品,第六次购买了1件商品,则第7次购买可以采取的策略有:买1件

通过上面分析,可知存在最优购买策略的情况如下:
①第一次购买1件商品+剩余6件商品的最优购买策略
②第一次购买2件商品+剩余5件商品的最优购买策略
③第一次购买3件商品+剩余4件商品的最优购买策略
④第一次购买4件商品+剩余3件商品的最优购买策略
⑤第一次购买5件商品+剩余2件商品的最优购买策略
⑥第一次购买6件商品+剩余1件商品的最优购买策略
⑦第一次购买7件商品
其中,情况①和情况⑥对应,情况②和情况⑤对应,情况③和情况④对应,也就是说,最多购买3次,即可找出最优的购买策略。

这里为了方便计算,计算了所有商品索引组合情况(买1件、买2件、买3件、买4件、买5件、买6件、买7件)的最少花费的钱数,然后按照购买次数的可能情况从全部商品索引组合中抽取符合条件的组合(1.不同组合之间没有交叉,2.全部组合的并集等于原始商品),最优的组合情况一定在其中,即可获得最少花费的钱数。
完整代码:

# -*- coding: utf-8 -*-
# @Time    : 8/20/2020 5:03 PM
# @Author  : Xinzhe
# @File    : lowest_money.py
# @Software: PyCharm
# 最少花的钱数

from itertools import combinations
import heapq
import random


# 定义一个类,存储各种组合以及所花费的钱数
class Combine_Price:
    def __init__(self, cpLen, cpIndex, cpPrice, cpValue):
        self.cpLen = cpLen  # 组合的长度
        self.cpIndex = cpIndex  # 组合中商品的索引
        self.cpPrice = cpPrice  # 组合中商品的价格
        self.cpValue = cpValue  # 组合花费的最少钱数


# 计算商品组合的所有策略中的最低价
def discount_func(subprices, discounts):
    len_sub_prices = len(subprices)
    lower = []
    for num in range(len(discounts)):
        if len_sub_prices >= discounts[num][0]:
            sum_lower = heapq.nsmallest(discounts[num][1], subprices)
            sum_prices = sum(subprices) - sum(sum_lower)
            lower.append(sum_prices)
        else:
            lower.append(sum(subprices))
    return min(lower)


# 购买K件商品的所有组合及最花费的钱数
def combines_prices(keys, n, prices, discounts):
    dict_count = []
    for number in range(1, n + 1):
        for index in combinations(keys, number):
            sub_prices = [prices[i] for i in index]
            lower_prices = discount_func(sub_prices, discounts)
            combine_price = Combine_Price(number, list(index), sub_prices, lower_prices)
            dict_count.append(combine_price)
    return dict_count


# 获取满足条件的组合方式以及所花费的最少钱数,要求如下;
# 1.组合中元素对象的索引没有交集,
# 2.组合中元素对象的索引并集等于商品索引集合
def get_lowest(keys, prices, discounts):
    n = len(prices)
    dict_counts = combines_prices(keys, n, prices, discounts)
    dict_counts_len = len(dict_counts)
    dict_keys = [x for x in range(dict_counts_len)]
    money = []
    combines = []
    max_money = sum(prices)
    for num in range(1, int(n / 2 + 1)):
        for id in combinations(dict_keys, num):
            sub_dict_counts = [dict_counts[i] for i in id]
            tmp_key = []
            tmp_value = 0
            tmp_combine = []
            for k in range(len(sub_dict_counts)):
                tmp_key = tmp_key + sub_dict_counts[k].cpIndex
                tmp_combine.append(sub_dict_counts[k].cpIndex)
                tmp_value += sub_dict_counts[k].cpValue
            if len(tmp_key) == n and set(tmp_key) == set(keys):
                # print("组合方式:{}, 花费的钱数:{}".format(tmp_combine, tmp_value))
                money.append(tmp_value)
                if tmp_value <= max_money:
                    combines = tmp_combine
            else:
                continue
    return (combines, min(money))


# 数据模拟
def data_simulate():
    n = random.randint(1, 2001)  # 1<=n<=2000
    m = random.randint(1, (10 ^ 5) + 1)  # 1<=m<=10^5
    prices = []
    for i in range(n):
        prices.append(random.randint(1, (10 ^ 5) + 1))  # 1<=ai<=10^5
    m_discount = []
    for i in range(m):
        x = random.randint(1, n + 1)  # 1<=x<=n
        y = random.randint(0, x + 1)  # 0<=y<=x
        m_discount.append([x, y])
    # 创建商品序号索引
    keys = [x for x in range(n)]
    print("商品个数:{}".format(n))
    print("优惠策略数:{}".format(m))
    print("商品价格:{}".format(prices))
    print("优惠策略:{}".format(m_discount))
    print()
    combine, res = get_lowest(keys, prices, m_discount)
    print("最优购买方式:{}".format(combine))
    print("最少花的钱数:{}".format(res))


if __name__ == '__main__':
    n, m = map(int, input().split(" "))
    prices = list(map(int, input().split(" ")))
    m_discount = []
    for i in range(m):
        m_discount.append(list(map(int, input().split(" "))))
    # 创建商品序号索引
    keys = [x for x in range(n)]
    print("商品个数:{}".format(n))
    print("优惠策略数:{}".format(m))
    print("商品价格:{}".format(prices))
    print("优惠策略:{}".format(m_discount))
    print()
    combine, res = get_lowest(keys, prices, m_discount)
    print("最优购买方式:{}".format(combine))
    print("最少花的钱数:{}".format(res))
    # for i in range(5):
    #     print("第{}次模拟...开始".format(i))
    #     data_simulate()
    #     print("第{}次模拟...结束".format(i))

题目3:粉刷墙壁

题目描述:小明喜欢粉刷墙壁,他每次会挑选一面 N × M N \times M N×M大小的白色墙壁进行粉刷。每一次粉刷,小明都会使用一种新的颜色,从一个格子开始,要么横着刷一段长度的墙壁,要么竖着刷一段长度的墙壁。(刷过的格子颜色会被覆盖)
颜色用小写字母表示,小明会按照’a’颜色,'b’颜色,‘c’颜色的顺序依次粉刷,因为只有26个英文字母,所以小明最多可以粉刷26次。(小明也可以不粉刷)
现在有一面被粉刷好的墙壁,小林想知道,这面墙是否是小明粉刷的。
输入描述:
第一行为一个数T,表示数据组数,接下来是T组数据。(1<=T<=5)
数据的第一行是两个整数n,m,表示粉刷好的墙壁的大小。(1<=n,m<=1000)
接下来的n行,每行m个字母,表示该格子的颜色。(.表示白色)
输出描述:
对于每组数据,如果是小明刷的,输出YES,否则输出NO。
示例1:
输入:
1
3 4
a a b a aaba aaba
. c b . .cb. .cb.
. c . . .c.. .c..
输出
YES

示例2:
输入:
2
2 2
. . .. ..
. . .. ..
3 5
. . . c . ...c. ...c.
. . a a . ..aa. ..aa.
. . b c . ..bc. ..bc.
输出
YES
NO

解题思路:
对于本题来说,分析过程很重要。概括来讲,就是把墙壁上的所有色块还原,如果能全部还原为白色,就说明是YES,否则就是NO。

  1. 创建一个类,记录墙壁的大小、墙壁的内容,以及其中的色块第一次出现时的坐标(irow, icol)
  2. 输入数据,将每组数据的大小、内容以及色块的坐标放入一个字典中
  3. 对于字典中的每组数据:
    1. 根据色块从a到z的顺序依次遍历
    2. 因为我们记录了色块第一次出现的坐标,也就是说,我们只需要判断该坐标的右侧、下侧是否可以连成线即可
    3. 因为要满足小明粉刷墙壁的规则,即每个色块在其行和列上出现的次数满足一定的规则:
      • 当在某一行或列上,该色块出现的次数均大于1,一定为NO
      • 当某一行上,该色块出现的次数大于1,且满足右侧可达性,则将该色块相同的区域重新置为白色(’.’)
      • 当某一列上,该色块出现的次数大于1,且满足下侧可达性,则将该色块相同的区域重新置为白色(’.’)
      • 当该色块只有1个时,将该区域置为白色
    4. 如果遍历完所有色块,该墙壁还存在色块,则为NO,否则为YES
  4. 返回每组数据的判断结果

完整代码:

# 粉刷墙壁
import numpy as np
import random


class Data_Object:
    def __init__(self, size, value, coordinates):
        self.size = size
        self.value = value
        self.coordinates = coordinates


# 打印输入的数据信息
def data_info(T, data):
    print("数据组数为:{}".format(T))
    for i in range(len(data)):
        print("第{}组数据大小为:{} * {}".format(i + 1, data[i].size[0], data[i].size[1]))
        print("数据内容为:\n{}".format(data[i].value))
        print("字母坐标为:\n{}".format(data[i].coordinates))


# 判断是否全部为"."号
def isDotAll(data):
    if ('.' == data).all():
        return True
    else:
        return False


# 根据字符的坐标进行扫描
def remove_specialchar(n, m, wall, char, irow, icol, direction):
    # 横向替换特定字符
    if "row" == direction:
        for i in range(icol, m):
            tmp_char = wall[irow, i]
            if tmp_char == char:
                wall[irow, i] = '.'
    # 纵向替换特定字符
    if "col" == direction:
        for i in range(irow, n):
            tmp_char = wall[i, icol]
            if tmp_char == char:
                wall[i, icol] = '.'
    # 替换单个字符
    if "one" == direction:
        wall[irow, icol] = '.'
    return wall


# 判断当前行或列是否可连续
def isReachable(n, m, char, wall, irow, icol, direction):
    # 按行判断是否可达
    flag = True
    if "row" == direction:
        for i in range(icol, m):
            tmp_char = wall[irow, i]
            if (tmp_char >= char) or (tmp_char == '.' and isDotAll(wall[irow, i:m])):
                continue
            else:
                flag = False
                break
    # 按列判断是否可达
    if "col" == direction:
        for i in range(irow, n):
            tmp_char = wall[i, icol]
            if (tmp_char >= char) or (tmp_char == '.' and isDotAll(wall[i:n, icol])):
                continue
            else:
                flag = False
                break
    return flag


def isYESorNO(data):
    oridinate = data.coordinates
    wall = data.value
    n, m = data.size
    if isDotAll(wall):
        return 'YES'
    characters = list(oridinate.keys())
    characters.sort(key=str.lower)
    flag = True
    for i in range(len(characters)):
        char = characters[i]
        irow = oridinate[characters[i]][0]  # 行坐标
        icol = oridinate[characters[i]][1]  # 列坐标
        count_row_char = np.sum(wall[irow, icol:] == char)
        count_col_char = np.sum(wall[irow:, icol] == char)
        # 两个方向上有相同的字母
        if count_row_char > 1 and count_col_char > 1:
            flag = False
            break
        elif count_row_char == 1 and count_col_char == 1:
            wall = remove_specialchar(n, m, wall, char, irow, icol, "one")
            continue
        # 横向存在多个相同字母,判断是否可达
        elif count_row_char > 1:
            direction = "row"
            if isReachable(n, m, char, wall, irow, icol, direction):
                wall = remove_specialchar(n, m, wall, char, irow, icol, direction)
                continue
            else:
                flag = False
                break
        # 纵向存在多个相同字母,判断是否可达
        elif count_col_char > 1:
            direction = "col"
            if isReachable(n, m, char, wall, irow, icol, direction):
                wall = remove_specialchar(n, m, wall, char, irow, icol, direction)
                continue
            else:
                flag = False
                break
        else:
            flag = False
            break

    if flag is False:
        return 'NO'
    else:
        if isDotAll(wall):
            return 'YES'
        else:
            return 'NO'


def main(data):
    res = []
    for i in range(len(data)):
        res_i = isYESorNO(data[i])
        res.append(res_i)
    for i in range(len(res)):
        print(res[i])


# 数据模拟
def simulation():
    T = random.randint(1, 5)
    data = {}
    for i in range(T):
        data[i] = Data_Object(size=None, value=None, coordinates=None)
    sequences = [chr(i) for i in range(97, 123)]
    fullseq = sequences + ['.']
    for i in range(len(data)):
        characters = set(sequences)
        n, m = [random.randint(1, 1000), random.randint(1, 1000)]
        wall = []
        dict_char = {}
        for j in range(n):
            tmp_data = []
            for k in range(m):
                tmp_data += list(random.sample(fullseq, 1))
            wall.append(tmp_data)
            tmp_data_list = list(set(tmp_data))
            for k in range(len(tmp_data_list)):
                char = tmp_data_list[k]
                char_index = tmp_data.index(char)
                if char in characters:
                    dict_char[char] = (j, char_index)
                    characters.remove(char)
        data[i] = Data_Object(size=[n, m], value=np.asmatrix(wall), coordinates=dict_char)
    return (T, data)


if __name__ == '__main__':
    T = int(input("请输入数据的组数:"))
    data = {}
    for i in range(T):
        data[i] = Data_Object(size=None, value=None, coordinates=None)
    for i in range(len(data)):
        characters = set([chr(i) for i in range(97, 123)])
        print("第{}组数据".format(i + 1))
        n, m = map(int, input("请输入粉刷好的墙壁的大小:").split(" "))
        wall = []
        dict_char = {}
        for j in range(n):
            tmp_data = list(input())
            wall.append(tmp_data)
            tmp_data_list = list(set(tmp_data))
            for k in range(len(tmp_data_list)):
                char = tmp_data_list[k]
                char_index = tmp_data.index(char)
                if char in characters:
                    dict_char[char] = (j, char_index)
                    characters.remove(char)
        data[i] = Data_Object(size=[n, m], value=np.asmatrix(wall), coordinates=dict_char)
    # data_info(T, data)
    main(data)
    # print("============数据模拟==========")
    # for i in range(3):
    #     print("第{}次模拟...Start".format(i))
    #     T, data = simulation()
    #     data_info(T, data)
    #     main(data)
    #     print("End")
    # print("============模拟完成==========")

题目4:被n到达的数

题目描述:定义一个函数f(x)的操作:将x+1,如果结果有后缀0,则把后缀0全部去掉,比如:
f(399) = 4; 399+1=400 =>4
f(2) = 3; 2+1=3
f(1099)=11; 1099+1=1100 => 11
我们定义,如果通过对数x执行f函数的操作数次后能够得到数y,那么数y就是可以被数x到达。
你有一个数n,需要求出有多少个不同的数能被数n到达。
输入描述:
一行一个整数n。(1<=n<=10^9)
输出描述:
一行一个整数,表示不同的数字的个数
示例:
输入:
1098
输出:
20

解题思路:
按照题目描述编写相关函数,组装起来即可,但是显然,题目出错了,示例中的输入结果应为19
完整代码:

# 被n到达的数
import random


def func(x):
    x = x + 1
    while x % 10 is 0:
        x = int(x / 10)
    return x


def reachable_num(n):
    nums = set()
    while True:
        n = func(n)
        if n not in nums:
            nums.add(n)
        else:
            break
    return nums


def simulation():
    n = random.randint(1, 1000000000)
    return n


if __name__ == '__main__':
    n = int(input('请输入一个整数:'))
    res = reachable_num(n)
    print(len(res))
    # print("==============数据模拟==============")
    # for i in range(10):
    #     n = simulation()
    #     res = reachable_num(n)
    #     print("第{}次模拟,能被{}到达的数的个数为{},值为:{}".format(i, n, len(res), res))
    # print("==============模拟结束==============")

你可能感兴趣的:(编程题,贪心,动态规划,迭代,递归,字符串截取)