D15-Acwing-3.2-4.1代码实现

今天主要是复习!!!先不看dp!先把代码都实现了,把之前的弄熟了弄懂了,不要求快!!!

SPFA求负环、

已经看不懂笔记了,看一下别人的题解和代码

嗯,具体思路是:
统计某个点中最短路包含边数,如果大于n-1,则说明存在负环。

嗯,遇到了一个问题,为什么大家都直接把整个队列插入,在题解里找到了答案。

初始时将所有点插入队列中可以按如下方式理解:
在原图的基础上新建一个虚拟源点,从该点向其他所有点连一条权值为0的有向边。那么原图有负环等价于新图有负环。此时在新图上做spfa,将虚拟源点加入队列中。然后进行spfa的第一次迭代,这时会将所有点的距离更新并将所有点插入队列中。执行到这一步,就等价于视频中的做法了。那么视频中的做法可以找到负环,等价于这次spfa可以找到负环,等价于新图有负环,等价于原图有负环。得证。

本来有一个问题:
设一个虚拟源点,那所有点的最短路到它不都为0了,吗?还算啥?
其实不是,我们这里在求负环,不在意最短路的真实值,如果有负环存在,最短路肯定会小于0的。
看一下自己的代码哪里错了。

N,M = 2010,10010
# e和ne是与边有关的
h,e,ne,idx = [-1]*N,[0]*M,[0]*M,0
st,dist,cnt,w = [True]*N,[0]*N,[0]*N,[0]*M
def add(a,b,c):
    global idx
    idx += 1
    e[idx] = b
    ne[idx] = h[a]
    h[a] =idx
    w[idx] = c
    
def spfa():
    q = [x for x in range(1,n+1)]
    tt,hh = 0,n-1
    dist[1] = 0
    while hh<=tt:
        t = q[hh]
        hh+=1
        st[t]=False
        i = h[t]
        while i != -1:
            j = e[i]
            if dist[j]>dist[t] + w[i]:
                dist[j] = dist[t] + w[i]
                cnt[j] = cnt[t]+1
                if cnt[j]>=n:
                    return True
                if not st[j]:
                    q.append(j)
                    tt +=1
                    st[j] = True
            i = ne[i]
    return False
n,m = map(int,input().split())
for _ in range(m):
    a,b,c = map(int,input().split())
    add(a,b,c)
if spfa():
    print('Yes')
else:
    print('No')

无语了,找不出错误来。
aaaaaaaaa是队列的tt和hh写反了

N,M = 2010,10010
# e和ne是与边有关的
h,e,ne,idx = [-1]*N,[0]*M,[0]*M,0
st,dist,cnt,w = [True]*N,[0]*N,[0]*N,[0]*M
def add(a,b,c):
    global idx
    idx += 1
    e[idx] = b
    ne[idx] = h[a]
    h[a] =idx
    w[idx] = c

    
def spfa():
    q = [x for x in range(1,n+1)]
    tt,hh = n-1,0
    dist[1] = 0
    while hh<=tt:
        t = q[hh]
        hh+=1
        st[t]=False
        i = h[t]
        while i != -1:
            j = e[i]
            if dist[j]>dist[t] + w[i]:
                dist[j] = dist[t] + w[i]
                cnt[j] = cnt[t]+1
                if cnt[j]>=n:
                    return True
                if not st[j]:
                    q.append(j)
                    tt +=1
                    st[j] = True
            i = ne[i]
    return False
    
n,m = map(int,input().split())

for _ in range(m):
    a,b,c = map(int,input().split())
    add(a,b,c)
    
if spfa():
    print('Yes')
else:
    print('No')

也就是说,这题判断负环,构建了一个到任何一个点的距离都是0的虚拟点,然后先处理一次,也就是初始化所有距离为0,然后把所有点放入队列,进行负环判断。有无该点不影响该图有无负环。

进军下一题!!!

Floydt854

用邻接矩阵进行储存
三层从1到n的循环
最内层判断是否更新g[i][j]

N,null = 210, 0x3f3f3f3f

n,m,k = map(int,input().split())
grid = [[null]*N for _ in range(N)]

def floyd():
    for k in range(1,1+n):
        for i in range(1,1+n):
            for j in range(1,1+n):
                grid[i][j] = min([grid[i][j],grid[i][k]+grid[k][j]])
for i in range(N):
    grid[i][i] = 0
for _ in range(m):
    x,y,z = map(int,input().split())
    grid[x][y] = min(grid[x][y],z)
floyd()
for _ in range(k):
    x,y = map(int,input().split())
    if grid[x][y]>null/2:
        print('impossible')
    else:
        print(grid[x][y])

到3.3啦~
由最短路径,进军最小生成树,棒棒!!!腿麻了
稠密用prim
稀疏图用kruskal
最小生成树问题就相当于连通所需最短的路径和问题

朴素prim【一般不用堆优化】

N,null = 510,0x3f3f3f3f
st,dist,g = [False]*N, [null]*N,[[null]*N for _ in range(N)]

n,m = map(int,input().split())
for _ in range(m):
    a,b,c = map(int,input().split())
    g[a][b] = min(g[a][b],c)
    # 无向图
    g[b][a] = g[a][b]
    
def prim():
    res = 0
    for i in range(n):
        t = -1
        for j in range(1,n+1):
            if not st[j] and (t==-1 or dist[t]>dist[j]):
                t = j
        if i and dist[t]==null:
            return null
        
        if i:
            res += dist[t]
        st[t] = True
        for j in range(1,n+1):
            dist[j] = min(dist[j],g[t][j])
    return res
res = prim()
if res==null:
    print('impossible')
else:
    print(res)

Kruskal

采用边的角度,先将所有边从小到大排序
然后使用并查集,放入一个集合中,并统计边的数量
最后通过边的数量判断是否生成了树

N,M = 100010,200020

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

n,m = map(int, input().split())
# 初始化并查集——查找两点是否在一个集合/将两点加入一个集合
p,edge = [i for i in range(n+1)],[]

for _ in range(m):
    a,b,c = map(int,input().split())
    edge.append([a,b,c])
# 从小到大排序
edge.sort(key = lambda x: x[2])

res, cnt = 0, 0
for i in range(m):
    a,b,w = edge[i]
    if find(a)!=find(b):
        p[find(a)] = find(b)
        res += w
        cnt += 1
if cnt == n-1:
    print(res)
else:
    print('impossible')



下面进军二分图啦!!!
发现了一个大佬,好厉害,我要向他学习呜呜呜!

二分图,主要是指图中的点可以划分到两个集合内,且集合内无边。

染色法

染色法可以判断是否为二分图
记住:二分图当且仅当图中不含奇数环

染色法
将所有点分成两个集合,使得所有边只出现在集合之间,就是二分图
二分图:一定不含有奇数环,可能包含长度为偶数的环, 不一定是连通图
dfs版本
代码思路:
染色可以使用1和2区分不同颜色,用0表示未染色
遍历所有点,每次将未染色的点进行dfs, 默认染成1或者2
由于某个点染色成功不代表整个图就是二分图,因此只有某个点染色失败才能立刻break/return
染色失败相当于存在相邻的2个点染了相同的颜色

python dfs超时,不过代码放过来

N,M =100010,200020
h,e,ne,idx = [-1]*N,[0]*M,[0]*M, 0
color = [0]*N

n,m = map(int,input().split())
def add(a,b):
    global idx
    idx += 1
    e[idx] = b
    ne[idx] = h[a]
    h[a] =idx
for _ in range(m):
    a,b = map(int,input().split())
    add(a,b)
    add(b,a)


# dfs给u点染色c,返回染色是否成功   
def dfs(u ,c):
    color[u] = c
    i = h[u]
    while i!=-1:
        j = e[i]
        if not color[j]:
            if not dfs(j, 3-c):
                return False
        elif color[j]==c:
            return False
        i = ne[i]
    return True
flag = True
for i in range(1,n+1):
    if not color[i]:
        if not dfs(i,1):
            flag = False
            break

if flag:
    print('Yes')
else:
    print('No')

感觉bfs比dfs简单,为什么bfs不会爆炸呢

N,M =100010,200020
h,e,ne,idx = [-1]*N,[0]*M,[0]*M, 0
color = [0]*N

n,m = map(int,input().split())
def add(a,b):
    global idx
    idx += 1
    e[idx] = b
    ne[idx] = h[a]
    h[a] =idx
for _ in range(m):
    a,b = map(int,input().split())
    add(a,b)
    add(b,a)


def bfs(u):
    hh,tt = 0,-1
    q = []
    q.append((u,1))
    tt += 1
    while hh<=tt:
        node,c = q[hh]
        hh+=1
        color[node] = c
        i = h[node]
        while i!=-1:
            j = e[i]
            if not color[j]:
                q.append((j,3-c))
                tt+=1
            elif color[j]==c:
                return False
            i = ne[i]
    return True
    
flag = True
for i in range(1,n+1):
    # 确保每个点都染色了
    if not color[i]:
        if not bfs(i):
            flag = False
            break

if flag:
    print('Yes')
else:
    print('No')

下面要去匈牙利了呀哈哈

匈牙利算法

这个算法是男女相亲的哈哈哈——没有两条边共用一个点,最多配多少对。
这题好有意思,找姑娘,也不是很难。
思路就是,先找心仪的姑娘,如果被找了,就去找那个男的,如果那个男的还能找的其他姑娘,这个姑娘就让给你,实在不行你再找其他备选姑娘。

N,M = 510, 100010
h,e,ne,idx = [-1]*N, [0]*M, [0]*M,0
match = [0]*N

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

def find(u):
    i = h[u]
    while i!=-1:
        j = e[i]
        if not st[j]:
            st[j] = True
            if match[j] == 0 or find(match[j]):
                match[j] = u
                return True
        i = ne[i]
    return False

n1,n2,m = map(int,input().split())
for _ in range(m):
    a,b = map(int,input().split())
    add(a,b)

res = 0
for i in range(1,n1+1):
    st = [False]*N
    if find(i):
        res +=1
print(res)

下面到数学知识啦!!!!!!!!!!

试除法求质数

一个数的因数都是成对出现的:例如12的因数有3和4,2和6
所以我们可以只枚举较小的那一个,即根下n,假设较小的为d,较大的为n/d;

不难哦

n = int(input())
def is_prime(a):
    if a<2:
        return False
    # 约数是成对出现的,只需判断小的即可。
    i = 2
    while i<=a/i:
        if a%i ==0:
            return False
        i+=1
    return True
        
for _ in range(n):
    a = int(input())
    if is_prime(a):
        print('Yes')
    else:
        print('No')

分解质因数

什么是分解质因数呢
阿阿阿
原来是这个意思啊
1、根据算术基本定理,不考虑排列顺序的情况下,每个正整数都能够以唯一的方式表示成它的因数的乘积。
n=p1^a1 * p2^a2 *p3a3…pnan

2、这里有个性质:n中最多只含有一个大于sqrt(n)的因子。证明通过反证法:如果有两个大于sqrt(n)的因子,那么相乘会大于n,矛盾。证毕

y总讲的时候,我听的迷迷糊糊。现在算是明白了什么叫做分解质因数。
具体思路就是,从2开始到根号n,因为不import包,所以用while循环来做。
一个个试着除。除好以后,如果x仍旧大于1,则说明存在一个大于根号n的质因子。

``
bash
n = int(input())

def divide(x):
k=2
while k< x/k+1:
if x == 1:
break
if x%k == 0:
s = 0
while x%k == 0:
x/=k
s += 1
print(k,s)
k +=1
if x>1:
print(int(x),1)
print()

for _ in range(n):
x = int(input())
divide(x)

朴素筛法

N = 1000010
is_prime = [True]*N
#朴素筛法
def find_prime(n):
    cnt = 0
    for x in range(2,n+1):
        if is_prime[x]:
            cnt+=1
        for i in range(2*x,n+1,x):
            is_prime[i] = False
    return cnt

n = int(input())
print(find_prime(n))

埃氏筛法

N = 1000010
is_prime = [True]*N
#朴素筛法-把所有数自己的倍数删除
# def find_prime(n):
#     cnt = 0
#     for x in range(2,n+1):
#         if is_prime[x]:
#             cnt+=1
#         for i in range(2*x,n+1,x):
#             is_prime[i] = False
#     return cnt

# 埃氏筛法:把所有质数的倍数删除
def find_prime(n):
    cnt=0
    for x in range(2,n+1):
        if is_prime[x]:
            cnt+=1
        else:
            continue
        for i in range(2*x,n+1,x):
            is_prime[i] = False
    return cnt
n = int(input())
print(find_prime(n))

线性筛法

任何一个合数都有最小质因子,也都会被最小值因子一次性筛掉。

线性看不懂哇~
今天给小朋友们弄读书评分的那个弄了好久,计划被打乱了不少哎哎哎啊啊

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值