D11-AcWing-复习835-841&&第三讲

33 篇文章 0 订阅
23 篇文章 0 订阅

今天先来复习昨天的老大难习题

835

不熟,晚上有时间再来一遍

# Trie树用来快速储存、查询字符串集合的
N =100010
son,cnt,idx = [[0]*26 for _ in range(N)], [0]*N,0

def insert(x):
    global idx
    p=0
    for i in range(len(x)):
        u = ord(x[i])-ord('a')
        if not son[p][u]:
           idx+=1
           son[p][u] = idx
        p = son[p][u]
    cnt[p] += 1
    
def query(x):
    global idx
    p=0
    for i in range(len(x)):
        u = ord(x[i])-ord('a')
        if not son[p][u]:
            return 0
        p = son[p][u]
    return cnt[p]
        
n = int(input())
for _ in range(n):
    op,x = input().split()
    if op=='I':
        insert(str(x))
    else:
        print(query(str(x)))

836并查集

还是比较丝滑的

# 并查集用来解决合并集合+查询两个数是否在同一个集合中
N = 100010
# 对于每个点来说,初始情况下,自己就是自己的父节点
p = [x for x in range(N)]

def find(x):
    if p[x]!=x:
        p[x] = find(p[x])
    return p[x]

n,m = map(int, input().split())

for _ in range(m):
    op,a,b = input().split()
    if op == 'M':
        p[find(int(a))] = find(int(b))
    else:
        if find(int(a))==find(int(b)):
            print('Yes')
        else:
            print('No')

837

1、注意先加size,不然连上了,就是同一个祖宗节点,加也白加了
2、两个不在同一个集合内,才相连;在一个集合内,不需要操作

N = 100010
p = [x for x in range(N)]
size = [1]*N

def find(x):
    if p[x]!=x:
        p[x] = find(p[x])
    return p[x]

n,m = map(int,input().split())

for _ in range(m):
    op, *q = input().split()
    if op == 'C':
        a,b=int(q[0]),int(q[1])
        if find(a)!=find(b):
            size[find(b)] +=size[find(a)]
            p[find(a)] = find(b)

    elif op =='Q1':
        a,b = map(int,q)
        if find(a)==find(b):
            print('Yes')
        else:
            print('No')
    else:
        a = int(q[0])
        print(size[find(a)])
        

838

没那么难。
down()括号内是节点的编号

# 堆排序一般来解决排序问题,默认是小根堆
# 实现方法为一维数组
N = 100010
n,m = map(int,input().split())
heap = [0]*N
# 从1开始是因为从0开始不方便表示儿子节点
heap[1:n+1] = [int(x) for x in input().split()] 
def down(u):
    t = u 
    if 2*u<=n and heap[2*u]<heap[t]:
        t = 2*u
    if 2*u+1<=n and heap[2*u+1]<heap[t]:
        t = 2*u+1
    if t!=u:
        heap[t],heap[u]=heap[u],heap[t]
        down(t)

for i in range(int(n/2),0,-1):
    down(i)

for _ in range(m):
    print(heap[1], end=' ')
    heap[1] = heap[n]
    n-=1
    down(1)
    
    

839

不是特别理解,但是能基本写出来了
问题还是挺多的

1\py中整除是//
2、删除和修改的时候记得down一遍,up一遍
3、注意输入进来的是在堆中的坐标,还是第几个输入的值。swap函数和h【】里面的都是堆中的坐标,注意转换。

N = 100010
h,ph,hp,idx,size=[0]*N,[0]*N,[0]*N,0,0

n = int(input())
def down(k):
    t= k
    if 2*k<=size and h[2*k]<h[t]:
        t = 2*k
    if 2*k+1 <= size and h[2*k+1]<h[t]:
        t = 2*k+1
    if t!= k:
        swap(t,k)
        down(t)
def up(k):
    while k//2 and h[k//2]>h[k]:
        swap(k//2,k)
        k //=2
def swap(a,b):
    ph[hp[a]],ph[hp[b]] = b,a
    hp[a],hp[b] = hp[b],hp[a]
    h[a],h[b] = h[b],h[a]
    
for _ in range(n):
    op,*px = input().split()
    if op == 'I':
        x = int(px[0])
        idx +=1
        size+=1
        h[size] = x
        ph[idx] = size
        hp[size] = idx
        up(size)
    elif op == 'PM':
        print(h[1])
    elif op=='DM':
        swap(size,1)
        size-=1
        down(1)
    elif op=='D':
        k = ph[int(px[0])]
        swap(size,k)
        size-=1
        down(k)
        up(k)
    else:
        k,x = ph[int(px[0])],int(px[1])
        h[k] = x
        down(k)
        up(k)

840

0x3f3f3f3f是一个很大的数,大于n可能出现的范围,所以当为这个数时,就是未被使用。是0f哦,不是of。
注意取mod的问题a-n*int(a/n)
k=mod(mod(x,N)+N,N)

然后对k+=1

# 哈希表主要用于把一个较大分散范围投射到娇小的范围
# 蹲坑法经验值Wie原来的两到三倍,然后是质数
N,null = 200003,0x3f3f3f3f
h = [null]*N

n = int(input())
def mod(a,n):
    return a-n*int(a/n)
    
def find(x):
    k = mod(mod(x,N)+N,N)
    while h[k]!=null and h[k]!=x:
        k+=1
        if k==N:
            k=0
    return k
            
for _ in range(n):
    op,px = input().split()
    px = int(px)
    k = find(px)
    if op == 'I':
        h[k] = px
    else:
        if h[k]==null:
            print('No')
        else:
            print('Yes')

python中不存在负数取模的问题,也就是取的模就是正数,直接用%就可以、。

# 哈希表主要用于把一个较大分散范围投射到娇小的范围
# 蹲坑法经验值Wie原来的两到三倍,然后是质数
N,null = 200003,0x3f3f3f3f
h = [null]*N

n = int(input())
# def mod(a,n):
#     return a-n*int(a/n)
    
def find(x):
    k = x%N
    while h[k]!=null and h[k]!=x:
        k+=1
        if k==N:
            k=0
    return k
            
for _ in range(n):
    op,px = input().split()
    px = int(px)
    k = find(px)
    if op == 'I':
        h[k] = px
    else:
        if h[k]==null:
            print('No')
        else:
            print('Yes')

在这里插入图片描述

843

getHash也要%Q,要不然估计数值太大了
另外,我感觉这里%Q并没有什么影响。

# 主要用于两个字符串是否相同
N = 100010
n,m = map(int,input().split())
def getHash(l,r):
    return (h[r]-h[l-1]*p[r-l+1])%Q
    
P,Q=131,2**64
p,h = [0]*N,[0]*N

p[0]=1
string =' ' + str(input())
for i in range(1,n+1):
    p[i] = (p[i-1]*P)%Q
    h[i] = (h[i-1]*P + ord(string[i]))%Q

for _ in range(m):
    l1,r1,l2,r2 = map(int,input().split())
    if getHash(l1,r1)==getHash(l2,r2):
        print('Yes')
    else:
        print('No')

还有一些之前的题没有做,先听课了,今天的任务是听完第三讲,听累了的时候再复习之前的题。

我的妈呀,3.1直接听自闭了哦~先写代码吧,心里有点怕怕,但是How hard could this be ??

842

N = 10
path, flag = [0]*N,[False]*N

def dfs(u):
    # 其实没有n位,因为从0开始填,只不过u==n时,相当于填满了,直接输出就ok了
    if u==n:
        for i in range(n):
            print(path[i],end=' ')
        print()
        return
    for i in range(1,n+1):
        if not flag[i]:
            path[u]=i
            flag[i]=True
            dfs(u+1)
            flag[i]=False
            # path[u]不用恢复为0,因为会被覆盖

n = int(input())
dfs(0)

843

第一种顺序,遍历每列

N = 20
def dfs(u):
    if u==n:
        for i in range(n):
            print("".join(grid[i]))
        print()
        return 0
    for i in range(n):
        if not col[i] and not dg[n+u-i] and not udg[u+i]:
            col[i],dg[n+u-i],udg[u+i]= True,True,True
            grid[u][i] = 'Q'
            dfs(u+1)
            grid[u][i]='.'
            col[i],dg[n+u-i],udg[u+i]= False,False,False

n = int(input())        
grid = [['.']*n for _ in range(n)]
col, dg, udg = [False]*N,[False]*N,[False]*N
dfs(0)N = 20
def dfs(u):
    if u==n:
        for i in range(n):
            print("".join(grid[i]))
        print()
        return 0
    for i in range(n):
        if not col[i] and not dg[n+u-i] and not udg[u+i]:
            col[i],dg[n+u-i],udg[u+i]= True,True,True
            grid[u][i] = 'Q'
            dfs(u+1)
            grid[u][i]='.'
            col[i],dg[n+u-i],udg[u+i]= False,False,False

n = int(input())        
grid = [['.']*n for _ in range(n)]
col, dg, udg = [False]*N,[False]*N,[False]*N
dfs(0)

第二种顺序
好像也没有那么难,不过是找到一种顺序,然后就不断递推,恢复现场即可。【安慰一下自己】
这种python超时了

N = 20
 
def dfs(x,y,s):
    if y==n:
        x+=1
        y=0
 
    if x==n:
        if s==n:
            for i in range(n):
                print(''.join(grid[i]))
            print()
        return 0
 
    dfs(x,y+1,s)
 
    if not row[x] and not col[y] and not dg[n+y-x] and not udg[x+y]:
        row[x], col[y], dg[n+y-x], udg[x+y] = True, True, True, True
        grid[x][y] = 'Q'
        dfs(x,y+1,s+1)
        grid[x][y] = '.'
        row[x], col[y], dg[n + y - x], udg[x + y] = False, False, False, False
 
if __name__ == '__main__':
    n = int(input())
    grid = [['.']*n for _ in range(n)]
    row, col, dg, udg = [False]*N, [False]*N, [False]*N, [False]*N
    dfs(0,0,0)

844

1、这里使用了python内建的collections中的deque,可以用来表示队列和栈。
2、BFS的主要用途还是求最短的值。
大概模型是先初始进入队列
然后让t指向队头,根据队头进行扩展,放入队尾
直到队列为空,进行完毕
每个节点都有了各自的最短

# collections是Python内建的一个集合模块,提供了许多有用的集合类。
# deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈:
from collections import deque
directions = [(1,0),(-1,0),(0,1),(0,-1)]

def bfs():
    while q:
        tmp = q.popleft()
        for direction in directions:
            x,y = tmp[0]+direction[0],tmp[1]+direction[1]
            if x>=0 and x<n and y>=0 and y<m and g[x][y]==0 and d[x][y]==-1:
                d[x][y]=d[tmp[0]][tmp[1]]+1
                q.append((x,y))
    return d[-1][-1]

n,m = map(int,input().split())
g,d,q = [],[[-1]*m for _ in range(n)],deque()
for _ in range(n):
    g.append([int(x) for x in input().split()])
d[0][0]=0
q.append((0,0))
print(bfs())

846树的重心

感觉知乎大佬写的有点问题,应该先更新idx才对呀~以后还是多借鉴一下acwing上的叭

# 删掉树上的一点后,所剩子树的个数最大值 最小
N = 100010
ans = N

def add(a,b):
    global idx
    idx+=1
    e[idx] = b
    ne[idx] = h[a]
    h[a] = idx

def dfs(u):
    global ans
    st[u] = True
    
    sum,res = 1,0
    i = h[u]
    while i!=-1:
        j = e[i]
        if not st[j]:
            s = dfs(j)
            res = max(res,s)
            sum +=s
        i = ne[i]
    res = max(res,n-sum)
    ans = min(ans,res)
    return sum 
h,e,ne,idx = [-1]*N,[0]*2*N,[0]*2*N,0
st = [False]*N
n = int(input())

for i in range(n-1):
    a,b = map(int,input().split())
    add(a,b)
    add(b,a)

dfs(1)
print(ans)

847图中点的层次

感觉像是简单的BFS搜索,没那么难,有一点点懂了

from collections import deque
N =100010

n,m = map(int, input().split())
h,e,ne,idx = [-1]*N, [0]*N,[0]*N,0
q,d = deque(),[-1]*N
q.append(1)
d[1] = 0

def add(a,b):
    global idx
    idx+=1
    e[idx] = b
    ne[idx] = h[a]
    h[a] = idx
    
    
def bfs():
    while q:
        tmp = q.popleft()
        i = h[tmp]
        while i!=-1:
            j = e[i]
            if d[j]==-1:
                d[j] = d[tmp]+1
                q.append(j)
            i = ne[i]
    return d[n]
for _ in range(m):
    a,b = map(int, input().split())
    add(a,b)
    
print(bfs())

848 有向图的拓扑排序

有点懂了,但是不清楚如何保证每个j只会被append一次,因为d【j】只可可能为一次0?
所以要不然d【j】会被append因为==0;要不然不会,因为可能减少不到0

from collections import deque

N = 100010
h,e,ne,idx = [-1]*N,[0]*N,[0]*N,0
# d装的是入度
q,d,ans = deque(),[0]*N,[]

def insert(a,b):
    global idx
    idx += 1
    e[idx] = b
    ne[idx] = h[a]
    h[a] = idx
def topsort():
    for i in range(1,n+1):
        if not d[i]:
            q.append(i)
    
    while q:
        tmp = q.popleft()
        ans.append(tmp)
        i = h[tmp]
        while i!=-1:
            j = e[i]
            d[j]-=1
            if d[j]==0:
                q.append(j)
            i = ne[i]
    return len(ans)==n
n,m = map(int,input().split())
for i in range(m):
    a,b = map(int, input().split())
    insert(a,b)
    d[b]+=1
if topsort():
    print(' '.join(map(str,ans)))
else:
    print('-1')

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值