模板(二)
DFS 深度优先搜索
1 2 3 4
| void dfs(int u) { if(u==n) {
|
}
for (```)
{
if(```)
{
树的 DFS
1 2 3 4 5 6 7 8 9 10
| int dfs(int u) { pd[u] = true;
for (int i=h[u];i!=-1;i=ne[i]) { int j =e[i]; if(!st[j]) dfs(j); } }
|
BFS 宽度优先搜索
q.push({a,b});
while (!q.empty())
{
for(```)
{
if(```)
{
1 2 3 4 5
| q.push({x,y}); } } } }
|
拓扑排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| bool topsort() { for(int i=1;i<=n;i++) { if(d[i]==0) { q.push(i); } } while (!q.empty()) { int t=q.front(); q.pop(); ans[k++]=t; for(int i=h[t];i!=-1;i=ne[i]) { int j=e[i]; d[j]--; if(d[j]==0) { q.push(j); } } } return k==n; }
|
最短路问题
$n:$点 $m:边$
Dijkstra-朴素 $O(n^2+m)$
- 初始化距离数组, dist[1] = 0, dist[i] = inf;
- for n次循环 每次循环确定一个min加入S集合中,n次之后就得出所有的最短距离
- 将不在S中dist_min的点->t
- t->S加入最短路集合
- 用t更新到其他点的距离
Dijkstra-堆优化 $O(mlogm)$
- 利用邻接表,优先队列
- 在
priority_queue<PII,vector<PII>,greater<PII>> heap;
中将返回堆顶
- 利用堆顶来更新其他点,并加入堆中类似宽搜
Bellman_ford $O(nm)$
- 注意连锁想象需要备份,
struct Edge{int a,b,c} Edge[M];
- 初始化dist, 松弛
dist[x.b] = min(dist[x.b], backup[x.a]+x.w);
- 松弛k次,每次访问m条边
Spfa $O(n) ~ O(nm)$
- 利用队列优化仅加入修改过的地方
- for k次
- for 所有边利用宽搜模型去优化bellman ford算法
- 更新队列中当前点的所有出边
Floyd $O(n^3)$
- 初始化d
- k, i, j 去更新d
朴素 dijkstra $O(n^2+m)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| bool pd[N]; int dist[N]; int g[N][N]; int n,m;
void dijkstra() { memset(dist,0x3f,sizeof(dist)); dist[1]=0;
for(int i=0;i<n;i++) { int t=-1; for(int j=1;j<=n;j++) { if(!pd[j]&&(t==-1||dist[t]>dist[j])) { t=j; } }
pd[t]=true;
for(int j=1;j<=n;j++) { dist[j]=min(dist[j],dist[t]+g[t][j]); } } }
|
堆优化版 dijkstra $O(mlogn)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| void add(int a,int b,int c) { e[idx]=b; ne[idx]=h[a]; h[a]=idx; w[idx]=c; idx++; }
void dijkstra(int u) { memset(dist,0x3f,sizeof(dist)); priority_queue<PII,vector<PII>,greater<PII>> heap; dist[u]=0; heap.push({0,u});
while (!heap.empty()) { auto t=heap.top(); heap.pop(); int distance=t.first; int ver=t.second;
if(pd[ver]) continue; pd[ver]=true;
for(int i=h[ver];i!=-1;i=ne[i]) { int j=e[i]; if(dist[j]>distance+w[i]) { dist[j]=distance+w[i]; heap.push({dist[j],j}); } } } }
|
Bellman-Ford 算法 $O(nm)$
可以有负权边
解决有边数限制的最短路问题(如最多走k条边)
当边循环i次时,表示最多走i条边的最短路
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| struct Node { int a; int b; int w; }edge[M];
int dist[N]; int backup[N];
void bf(int u) { memset(dist,0x3f,sizeof(dist)); dist[u]=0;
for(int i=0;i<k;i++) { memcpy(backup,dist,sizeof(dist)); for(int j=0;j<m;j++) { int a=edge[j].a,b=edge[j].b,w=edge[j].w; dist[b]=min(dist[b],backup[a]+w); } } }
int main() { int a,b,w; cin>>n>>m>>k; for(int i=0;i<m;i++) { cin>>a>>b>>w; edge[i]={a,b,w}; } bf(1); if(dist[n]>0x3f3f3f3f/2) cout<<"impossible"<<endl<<endl; else cout<<dist[n]<<endl; return 0; }
|
SPFA 算法 $O(m) ~ O(nm)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| void add(int a,int b,int c) { e[idx]=b; ne[idx]=h[a]; h[a]=idx; w[idx]=c; idx++; }
void spfa(int u) { memset(dist,0x3f,sizeof(dist)); queue<int> q; dist[u]=0; q.push(u); pd[u]=true; while (!q.empty()) { int t=q.front(); q.pop(); pd[t]=false; for(int i=h[t];i!=-1;i=ne[i]) { int j=e[i]; if(dist[j]>dist[t]+w[i]) { dist[j]=dist[t]+w[i]; if(!pd[j]) { pd[j]=true; q.push(j); } } } } }
|
spfa 判断图中是否存在负环
因为点1可能到不了有负环的点, 因此把所有点都加入队列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| bool pd[N]; int h[N],e[N],ne[N],w[N],idx; int dist[N]; int cnt[N]; int n,m;
bool spfa() { queue<int> q; for(int i=1;i<=n;i++) { q.push(i); pd[i]=true; }
while (!q.empty()) { int t=q.front(); q.pop(); pd[t]=false; for(int i=h[t];i!=-1;i=ne[i]) { int 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(!pd[j]) { pd[j]=true; q.push(j); } } } } return false; }
|
floyd 算法 $O(n^3)$
多源最短路算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { if(i==j) d[i][j]=0; else d[i][j]=INF; } }
void floyd() { for(int k=1;k<=n;k++) { for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { d[i][j]=min(d[i][j],d[i][k]+d[k][j]); } } } }
|
最小生成树
prim 算法 $O(n^2)$
可类比朴素 $dijkstra$ 算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| bool prim(int u) { memset(dist,0x3f,sizeof(dist)); dist[u]=0;
for(int i=0;i<n;i++) { int t=-1; for(int j=1;j<=n;j++) { if(!pd[j]&&(t==-1||dist[t]>dist[j])) { t=j; } }
if(dist[t]==INF) return false;
pd[t]=true; ans+=dist[t];
for(int j=1;j<=n;j++) { dist[j]=min(dist[j],g[t][j]); } } return true; }
|
kruskal 算法 $O(mlogm)$
先把边按照权重排序
再从小到大枚举,如果这条边不在生成树中,则加上这条边(用并查集)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| bool kruskal() { sort(edge,edge+m,cmp);
for(int i=0;i<m;i++) { int a=edge[i].a,b=edge[i].b,w=edge[i].w; int pa=find(a),pb=find(b); if(pa!=pb) { p[pa]=pb; ans+=w; cnt++; } }
if(cnt==n-1) return true; else return false; }
|
二分图
(就是二部图…)
染色法判断二分图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| bool dfs(int u,int c) { colour[u]=c;
for(int i=h[u];i!=-1;i=ne[i]) { int j=e[i]; if(!colour[j]) { if(!dfs(j,3-c)) { return false; } }else if(colour[j]==c) { return false; } } return true; }
pd=1; for(int i=1;i<=n;i++) { if(!colour[i]) { if(!dfs(i,1)) { pd=0; } } } if(pd) cout<<"Yes"<<endl; else cout<<"No"<<endl;
|
匈牙利算法 $O(m∗n)$
前提是二分图
找到最大匹配(即最多有几对 “ 一对一 ”)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| bool find(int u) { for(int i=h[u];i!=-1;i=ne[i]) { int j=e[i]; if(!pd[j]) { pd[j]=true;
if(!match[j]||find(match[j])) { match[j]=u; return true; } } }
return false; }
for(int i=1;i<=n1;i++) { memset(pd,false,sizeof(pd));
if(find(i)) ans++; }
|