今天主要是复习!!!先不看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))
线性筛法
任何一个合数都有最小质因子,也都会被最小值因子一次性筛掉。
线性看不懂哇~
今天给小朋友们弄读书评分的那个弄了好久,计划被打乱了不少哎哎哎啊啊