头条算法题:产品经理,程序员任务调度、用户喜好值算法等等

思路是很简单的,就是以时间为主线,在当前时刻,先统计出所有该时刻提出的idea

然后以PM为类别分组这些idea即代码中的group

然后计算出每个PM最想先实现的idea,记录在IdeaPriority中

然后就是遍历每个程序员,只要程序员在该时刻空闲,就从IdeaPriority中按题目要求选出一个idea给其完成

遍历完每个程序员后也就是说在当前时刻遍历完了所有程序员,然后时刻+1,

继续循环,即统计出所有这新时刻下提出的idea,,,,,,,,,,

下面是python 版本,由于耗时只通过了60%,所以还是用C快一下,有时间再写一个c版本的吧!!!

N,M,P = [int(e) for e in input().split()]
idea = []
for i in range(P):
    idea.append([int(e) for e in input().split()]+[i])
idea.sort(key = lambda x:x[1])

finsh = 0                       #程序员完成的idea个数
NoTime = 1                      #当前时刻
last = 0                        #上一次idea进入的下标
Program = [0 for _ in range(M)] #程序员直到空闲的时刻
NoTimeIdea = []                 #当前时刻待解决的idea
result = {}                     #结果
group = {}                      #当前时刻各个程序员提出的idea集合


while finsh<P:
    #当前时刻待解决的所有idea
    for i in range(last,P):
        if idea[i][1]==NoTime:
            if idea[i][0] not in group:
                group[idea[i][0]] = [idea[i]]
            else:
                group[idea[i][0]].append(idea[i])
            if i==P-1:
                last = P
        else:
            last = i
            break
    #选出每个产品经理最想实现的idea
    IdeaPriority = []
    for j in group.keys():
        group[j].sort(key = lambda x:(-x[2],x[3],x[1]))
        IdeaPriority.append(group[j][0])
    #给每个程序员选出需要解决的idea
    for i in range(M):
        IdeaPriority.sort(key = lambda x:(x[3],x[0]))
        if Program[i]<=NoTime and len(IdeaPriority)!=0:
            Program[i] = NoTime+IdeaPriority[0][3]
            result[IdeaPriority[0][4]] = Program[i]
            j = IdeaPriority[0][0]
            index = IdeaPriority[0][4]
            #从IdeaPriority中删除已经解决的idea
            IdeaPriority.pop(0)
            #从group中删除已经解决的idea
            for p in range(len(group[j])):
                if group[j][p][4]==index:
                    group[j].pop(p)
                    if len(group[j])==0:
                        del group[j]
                    else:
                        group[j].sort(key = lambda x:(-x[2],x[3],x[1]))
                        IdeaPriority.append(group[j][0])
                    break
            finsh+=1
    NoTime+=1
for i in range(P):
    print(result[i])

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------

此题的关键是要读出如下信息:

因为一些特殊的原因,不会出现一个查询的用户区间完全覆盖另一个查询的用户区间

即我们可以先给整个查询按照第一个字段排序(即待查询用户的起始标号),这里采用升序

排序后注意:

只会出现如下区间错位:

即类如[2,6] [4,8]

而不会出现:

所以经过排序后,假设当前这一组的查询区间为 [L_now, R_now],紧挨着其下一组是[L_next, R_next]

那么一定是:

L_now<L_next<=R_now

R_now<R_next

好了,下面我们进入正题:

首先我们使用一个字典来记录当前待查询区间下每一个喜好值对应的人数

-------------------------------------------------------------------------------------------------------------------------------------

举例:有一组喜好值

1,4,3,5,5,7,3,5,9,10

可以看到喜好值有1,3,4,5,6,7,9,10这几种:

假设当前查询的区间是[2,6]即4,3,5,5,7那么字典中记录的应该是:

1:0

3:1

4:1

5:2

6:0

7:1

9:0

10 : 0

那么我们再看一下当前待查询区间要查询的喜好值,比如说是5,那我们从字典中找到对应的key下的value即可这里是2

---------------------------------------------------------------------------------------------------------------------------------------

所以我们就将待查询的数据一组组输入,然后统计该区间内的各个喜好值,最后对应查询即可

问题转化为怎么统计该区间内的各个喜好值,本题的巧妙之处就在于这里的动态规划:

假设现在我们统计的是下一组(图中下面一个条形框)

由于我们上一组已经统计过了(图中上面一个条形框),可以看到其实黄色部分是可以利用的,不用在重新统计,换句话说:

丢弃蓝色,保留黄色,再多统计一下灰色

这里采用的方法是:

遍历蓝色中的每一个元素,在字典中使该元素减一

遍历灰色中的每一个元素,在字典中使该元素加一

可以想象结果就是:

丢弃蓝色,保留黄色,多统计了一下灰色

代码如下:

_ = input()
arr = []
d = {}
for e in input().split():
    arr.append(int(e))
    d[int(e)] = 0

n = int(input())
temp = []
for i in range(n):
    e = input().split()
    e[0] = int(e[0])-1
    e[1] = int(e[1])-1
    e[2] = int(e[2])
    temp.append(e+[i])
temp.sort()

result = []
left = 0
right = -1
arr[0] = 1
for i in range(n):
    tempList = temp[i]
    left_interval = tempList[0] - left
    right_interval = tempList[1] - right
    for l in range(left_interval):
        d[arr[left+l]]-=1
    for r in range(right_interval):
        d[arr[right+r+1]]+=1
    if tempList[2] in d:
        result.append([d[tempList[2]],tempList[3]])
    else:
        result.append([0,tempList[3]])
    left = tempList[0]
    right = tempList[1]
    
result = sorted(result,key = lambda x:x[1])
for i in range(n):
    print(result[i][0])

代码中的d就是字典

 for l in range(left_interval):
        d[arr[left+l]]-=1

就是天蓝色

for r in range(right_interval):
        d[arr[right+r+1]]+=1

就是灰色

 if tempList[2] in d:
        result.append([d[tempList[2]],tempList[3]])
 else:
        result.append([0,tempList[3]])

就是记录一下当前这一组的查询结果

这里的tempList[3]只是为了便于结果按顺序输出,因为开头排了一下序,所以顺序是乱的,所以多加了一个索引即:

temp.append(e+[i])中的[i]

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------

该题的关键在于要准确找到到底一开始是哪个房间的人全部出来了(代码中的start就是该房间的索引)

根据题意我们很容易就可以确定一定是分配后所有房间的最小值,

假设最小值只有一个的时候,毫无疑问一开始就是这个房间的人全部出来了,

如果最小值有多个那么怎么确定呢?这也是本题的关键和难点所在:

这里就要结合题中给出的x进行综合判断得出了,现分成以下情况进行讨论:

当最小值的索引全在x的左边时:

那么start就是这些最小值集合的最后一个最小值的索引

当最小值的索引全在x的右边时:

那么start就是这些最小值集合的最后一个最小值的索引

当最小值的索引部分在x的右边部分在x左边时:

那么start就是左半部分最小值集合中最后一个元素的索引

总结来说就是:

先看x左半部分有没有元素,有的话就选左半部分最后一个元素作为start

没有的话(全在右面的话)我们就选最后一个元素索引作为start

至于为什么这样,稍微结合题意推断一下就可知道了,其中的逻辑道理倒不是很难,难就难在想不到这个解题的点。

确定了start那就好办了,同时我们也知道了最小值,这个最小值其实就代表了进行了几轮,代码中mintemp就是指的这里的最小值,进行了mintemp这么多轮,那么每个房间肯定都加了mintemp个人,所以先减去mintemp,同理start就出去了n*mintemp个人。

整轮考虑完了以后,我们再来看最后一个没有走完整轮的过程:

,,,,,,,,,startcur,,,,,,,,,,,,,end,,,,,,,,,,,,,,,,,,,,,,,

其实就是,,,,,,,,,,,,,这部分房间都再多减一就是对应其原来房间人数啦

需要注意的是有可能是下面这种情况:那么再多减一的房间就是下面红色的部分

,,,,,,,,,,,,,,,,,,end,,,,,,,,,,,,startcur,,,,,,,,,,,

代码中

while startcur!=x:
    startcur = (startcur+1)%n
    result[startcur]-=1
    result[start]+=1

就是对应这部分,可以看到startcur就是每次加一,不断向前搜索房间,知道遇到end

当然了我们最开始确定的start这个房间要单独特殊处理,其原来的人数应该是在n*mintemp基础上继续加人数,加多少呢?那就是看最后这次搜索的房间数啦,即result[start]+=1

最后附上全部代码:

n,x = [int(e) for e in input().split()]
x = x-1
mintemp = 10**9
home = []
homemin = []
for i,e in enumerate(input().split()):
    e = int(e)
    home.append(e)
    if e<mintemp:
        homemin = []
        homemin.append(i)
        mintemp = e
    if e==mintemp:
        homemin.append(i)
i = 0
while i<len(homemin) and homemin[i]<=x:
    i+=1
start = homemin[i-1]
startcur = start
result = [e-mintemp for e in home]
result[start] = n*mintemp
while startcur!=x:
    startcur = (startcur+1)%n
    result[startcur]-=1
    result[start]+=1
for e in result:
    print(e,end=' ')

说明一点:

巧妙之处在于

while i<len(homemin) and homemin[i]<=x:
    i+=1
start = homemin[i-1]

这里的代码作用就是在找start

可以看到其其实就是在搜索x左面元素集合的最后一个,即i-1就是最后一个元素

如果左边没有,那么i=0,i-1=-1,恰好选整个最小值集合的最后一个值作为start,满足我们上面的分析

这里也就是在编程方面绕了一下,最注意的逻辑还是上面我们分析的

-------------------------------------------------------------------------------------------------------------------------------------------------------------------

这题咋一看挺难的,其实是非常简单的,关键是看有没有分析出其中的关键点。

首先6个面转,其实就可以简化看做是三个面在转,比如我们考虑前,上,右

然后在这三个面中我们可以进一步简化:

比如说单看前面这一个面,其有2层即上下层,上层可以左右转90度,下层可以左右转90度,咋一看是4种情况,其实就是2种即上层右转90就相当于下层左转90,所以我们只要考虑一层就可以啦,比如说只考虑上层左转还是右转这2种情况,上右面也是如此。

综上所述,每一步中一共有3*2=6种情况,然后深度遍历即可

链接:https://www.nowcoder.com/questionTerminal/7f51d0abdfdf44d2a2a777ea35124eb2
来源:牛客网

def compute(A):
    return A[0]*A[1]*A[2]*A[3] + A[4]*A[5]*A[10]*A[11] + A[6]*A[7]*A[12]*A[13] + A[8]*A[9]*A[14]*A[15] + A[16]*A[17]*A[18]*A[19] + A[20]*A[21]*A[22]*A[23]
  
def DS(A,i):
    if i == 0:
        return
#六种情况进行旋转,每次转动12个。
    A1 = A.copy()
    A1[1], A1[3],A1[7], A1[13], A1[17], A1[19],A1[21],A1[23],A1[8], A1[9],A1[14],A1[15] = A1[7], A1[13], A1[17], A1[19],A1[21],A1[23], A1[1], A1[3],A1[14], A1[8],A1[15],A1[9]
    res.append(compute(A1))
    DS(A1,i-1)
      
    A2 = A.copy()
    A2[1], A2[3],A2[7], A2[13], A2[17], A2[19],A2[21],A2[23],A2[8], A2[9],A2[14],A2[15] = A2[21],A2[23], A2[1], A2[3], A2[7], A2[13], A2[17],A2[19],A2[9], A2[15],A2[8],A2[14]
    res.append(compute(A2))
    DS(A2,i-1)
     
    A3 = A.copy()
    A3[4], A3[5], A3[6], A3[7], A3[8], A3[9], A3[23], A3[22],A3[0], A3[1], A3[2], A3[3] = A3[6], A3[7], A3[8], A3[9], A3[23], A3[22], A3[4], A3[5],A3[2], A3[0], A3[3], A3[1]
    res.append(compute(A3))
    DS(A3,i-1)
     
    A4 = A.copy()
    A4[4], A4[5], A4[6], A4[7], A4[8], A4[9], A4[23], A4[22],A4[0], A4[1], A4[2], A4[3] =  A4[23], A4[22], A4[4], A4[5], A4[6], A4[7], A4[8], A4[9],A4[1], A4[3], A4[0], A4[2]
    res.append(compute(A4))
    DS(A4,i-1)
     
    A5 = A.copy()
    A5[2], A5[3], A5[8], A5[14], A5[17], A5[16], A5[11], A5[5],A5[6], A5[7], A5[12], A5[13] = A5[8], A5[14], A5[17], A5[16], A5[11], A5[5], A5[2], A5[3], A5[7], A5[13], A5[6], A5[12]
    res.append(compute(A5))
    DS(A5,i-1)
      
    A6 = A.copy()
    A6[2], A6[3], A6[8], A6[14], A6[17], A6[16], A6[11], A6[5], A6[6], A6[7], A6[12], A6[13] =  A6[11], A6[5], A6[2], A6[3], A6[8], A6[14], A6[17], A6[16], A6[12], A6[6], A6[13], A6[7]
    res.append(compute(A6))
    DS(A6,i-1)
      
    i += -1
      
row= [int(i) for i in input().strip().split()]
res = [compute(row)]
i = 5
DS(row,i)
print(max(res))

【编码题】字符串S由小写字母构成,长度为n。定义一种操作,每次都可以挑选字符串中任意的两个相邻字母进行交换。询问在至多交换m次之后,字符串中最多有多少个连续的位置上的字母相同?

思路就是遍历每一个字母,统计其在M步内所能构成的最大长度

在计算具体某一个字母下的最大长度时,应用动态规划:

利用该字母一共出现了6次,那么先计算每两个组合在一起的所有情况用到的最小步数,然后再动态考虑3个组合在一起的所有情况用到的最小步数,然后再动态考虑4个组合在一起的所有情况用到的最小步数,,,,,,等等

def Count(g,step):
    n = len(g)
    minstep = float('inf')
    dp = [[0 for _ in range(n)] for _ in range(n)]
    if n==1:
        return 1
    #所有2个连续相连所需要的互换次数
    for i in range(n-1):
        dp[i][i+1] = g[i+1]-g[i]-1
        minstep = min(minstep,dp[i][i+1])
    if minstep>step:
        return 1
    elif minstep==step:
        return 2
    else:
        #接着考虑3个连续的所有情况
        for i in range(2,n):
            minstep = float('inf')
            for j in range(n-i):
                row = j
                col = j+i
                # 这里较难理解,假设显现考虑这样一个情况:
                # 12,,,,,,,,56
                #,,,,是中间部分,即dp[row+1][col-1]
                #那么假设中间部分排列好后左面是q,右面是p
                #那么本轮移动12 和56 就共需要q-12-1 和56-p-1
                #化简后就是56-12+q-p-2,假设12和56的坐标是row,col
                #可以得到 col-row-2 = p-q 所以q-p-2 = row-col
                #其中56-12就是g[col]-g[row] 所以56-12+q-p-2 = g[col]-g[row]-(col-row)
                dp[row][col] = dp[row+1][col-1]+g[col]-g[row]-(col-row)
                minstep = min(minstep,dp[row][col])
            if minstep>step:
                return i
            if minstep==step:
                return i+1
    return n

S,m = [e for e in input().split()]
m = int(m)
group = {}

for i,s in enumerate(S):
    if s not in group:
        group[s] = [i]
    else:
        group[s].append(i)
        
result = 0
for key in group.keys():
    result = max(result,Count(group[key],m))
print(result)

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

此题是一个动态规划问题,所以关键在于找到递推式,分析如下:

假设第一次到达i这个房间的时候,其是第一次(奇数次),故下一步一定是返回到i之前的房间,经过一定步骤的时候再次回到i房间的时候,此次是偶数次,按照规则移动到下一个房间即i+1,此时对于i+1房间来说,其是第一次,又返回到i+1之前的房间,周而复始,,,,,,,,,,,,,,,,

通过上面的总结我们可以得到如下结论:

就拿第一次到达i房间来说,其先返回之前的某个房间,然后经过一系列步骤后再次回到i房间,注意此时i之前的所有房间被经过的次数都应该是偶数次,因为只有偶数次后,其才会向后走,然后一步步再次回到i

其实明白了这个规律,就好办了,现在假设dp[i]表示第一个房间到第i个房间所用的步数

f(i,j)表示第i房间到第j个房间所用的步数(注意这里所考虑的情况是当前i房间被访问的次数是奇数次)

home 是一个列表,保存的就是输入的第二行数据即房间之间的穿梭信息pi

所以比较容易得到:假设房间号i<=j

dp[i] = dp[j]+f(j,i) 进而可得f(j,i) = dp[i]-dp[j]

现在我们来看动态规划部分:即找dp[i]和dp[i-1]的关系式

dp[i] = dp[i-1] +f(i-1,i) = dp[i-1]+1+f(home[i-1],i-1)+1 = dp[i-1]+1 +dp[i-1]-dp[home[i-1]]+1=2dp[i-1] - dp[home[i-1]]+2

好啦,得到递推式:

dp[i]  = 2dp[i-1] - dp[home[i-1]]+2

分析结束

最后给一下全部代码:

n = int(input())
home = [int(e)-1 for e in input().split()]
dp = [0 for _ in range(n+1)]
dp[1] = 2
for i in range(2,n+1):
    dp[i] = 2*dp[i-1] - dp[home[i-1]]+2
print(dp[n]%1000000007)

注意:按照一般思路来说应该从i=1即第二个房间开始递推,而这里是从i=2即第三个房间开始的

那是因为:

比如看题目中给出的那个例子:

2

1 2

那么我们在求dp[1] 的时候会用到home[0] = 1 即dp[1] 所以要从第三个房间开始的

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------

此题比较简单,就是从左到右遍历每一个字母,以其为起点(假设当前起点为i)进行记录a和b的个数,当两者个数同时达到m上限时停止,此时以i为起点在m步操作数限制的情况下所能得到的最大长度就是遍历过的总长度。

下面用队列来实现这一过程:

当队列中a的个数和b的个数都大于m时我们才考虑出队列情况,否则都是进队列

当两者都达到m时,若当前待进队列的元素是a时,我们就让队首出一个a,当然了如果队首是b就一直出队列

若当前待进队列的元素是b时,道理同样

全部代码:

n,m = [int(e) for e in input().split()]
Str = input()
result = 0
a_count = 0
b_count = 0
q = []
for e in Str:
    if e=='a':
        a_count+=1
    if e=='b':
        b_count+=1
    if a_count>m and b_count>m:
        if e=='a':
            while True:
                if q[0]=='a':
                    q.pop(0)
                    break
                q.pop(0)
                b_count-=1
            a_count-=1
        else :
            while True:
                if q[0]=='b':
                    q.pop(0)
                    break
                q.pop(0)
                a_count-=1
            b_count-=1
    q.append(e)
    result = max(result,len(q))
print(result)

猜你喜欢

转载自blog.csdn.net/weixin_42001089/article/details/87114410