搜索与图论
目录
搜索与图论
DFS
概述
基本策略
BFS
概述
基本策略
图的两种存储方式
链式向前星
int h[N], ne[N], e[N], w[N], idx, n;
void add(int a, int b, int c)
{
ne[idx] = h[a], e[idx] = b, w[idx] = c, h[a] = idx++;
}
int main()
{
memset(h, -1, sizeof h);
有向图add(u, v), add(v, u);
无向图add(u, v);
}
邻接矩阵
图的DFS
树的重心
int dfs(int u)
{
st[u] = true;
int sum = 1, ans = 0;
for(int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
if(!st[j])
{
int s = dfs(j);
sum += s;
ans = max(ans, s);
}
}
ans = max(ans, n - sum);
res = min(res, ans);
}
图的BFS
int bfs()
{
memset(d, -1, sizeof d);
q.push(1); d[1] = 0;
while(q.size())
{
int t = q.front(); q.pop();
for(int i = h[t]; i != -1; i = ne[i])
{
int j = e[i];
if(d[j] == -1)
{
d[j] = d[t] + 1;
q.push(j);
}
}
}
return d[n];
}
拓扑排序
拓扑排序的实现
int topsort()
{
int qq = 0, tt = -1;
for(int i = 1; i <= n; i++) if(!st[i]) q[++tt] = i;
while(qq <= tt)
{
int top = q[qq++];
for(int i = h[top]; i != -1; i = ne[i])
{
int j = e[i];
st[j] --;
if(!st[j]) q[++tt] = j;
}
}
if(qq == n) return 1;
else return 0;
}
...
if(topsort()) for(int i = 0; i < n; i++) cout << q[i] << ' ';
else cout << -1;
利用拓扑排序判断环
拓扑排序失败就有环
最短路
Dijkstra
朴素
#include <bits/stdc++.h>
using namespace std;
const int N = 505, M = 1e5+5;
int g[N][N], dist[N], n, m;
bool st[N];
int dijkstra(int u, int v) //从u到v的最短路,稠密图
{
memset(dist, 0x3f, sizeof dist); //无穷大为不连通
dist[u] = 0;
for(int i=1; i<=n; i++) //进行n轮,每次选择一个点
{
int j = -1;
for(int i=1; i<=n; i++) //每次选择那个不在集合里面并且距离是最小的点。
{
if(!st[i] && (j==-1 || dist[j] > dist[i])) j=i;
}
//更新每个点新的距离
for(int i=1; i<=n; i++)
dist[i] = min(dist[i], dist[j]+g[j][i]);
st[j] = true;
}
return dist[v]==0x3f3f3f3f? -1: dist[v];
}
int main()
{
cin>>n>>m;
memset(g, 0x3f, sizeof g);
for(int i=1; i<=m; i++)
{
int u, v, w; cin>>u>>v>>w;
g[u][v] = min(g[u][v], w);
}
cout<<dijkstra(1, n);
}
堆优化
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5+5, M = 2 * N;
int h[N], ne[N], w[N], e[N], idx;
int n, m;
int d[N];
bool st[N];
typedef pair<int, int> PII; //优先以第一个关键字排序
void add(int a, int b, int c)
{
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
int dijkstra(int u, int v)
{
priority_queue<PII, vector<PII>, greater<PII>> q;
memset(d, 0x3f, sizeof d);
d[u] = 0;
q.push({0, u});
while(q.size())
{
auto v = q.top(); q.pop();
int ver = v.second, dist = v.first;
if(st[ver]) continue; //每次选择不在集合里的那个最小的点
st[ver] = true;
for(int i=h[ver]; i!=-1; i=ne[i])
{
int j=e[i];
if(d[j] > dist + w[i])
{
d[j] = dist+w[i];
q.push({d[j], j});
}
}
}
return d[v] == 0x3f3f3f3f? -1: d[v];
}
int main()
{
cin>>n>>m;
memset(h, -1, sizeof h);
for(int i=1; i<=m; i++)
{
int u, v, w; cin>>u>>v>>w;
add(u, v, w);
}
cout<<dijkstra(1, n);
}
bellman-ford
板子
应用: 求边数限制的最短路
#include <bits/stdc++.h>
using namespace std;
const int N = 505, M = 1e4+5;
int n, m, k;
int dist[N], backup[N];
struct Edge{
int u;
int v;
int w;
}edges[M];
int Bellman_Ford(int u, int v, int k) //k条边
{
memset(dist, 0x3f, sizeof dist);
dist[u] = 0;
for(int i=1; i<=k; i++) //每次多更新一条边,n个点最短路最多为n,否则成环
{
memcpy(backup, dist, sizeof dist);
for(int j=1; j<=m; j++) //bfs的思想更新
{
int u=edges[j].u, v=edges[j].v, w=edges[j].w;
dist[v] = min(dist[v], backup[u]+w);
}
}
if(dist[v] > 0x3f3f3f3f/2) return -1;
else return dist[v];
}
int main()
{
cin>>n>>m>>k;
for(int i=1; i<=m; i++)
{
cin>>edges[i].u>>edges[i].v>>edges[i].w;
}
int p = Bellman_Ford(1, n, k);
if(p == -1) cout<<"impossible";
else cout<<p;
}
spfa
板子
应用:求负权最短路
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+5;
int h[N], e[N], ne[N], w[N], idx;
int d[N];
bool st[N];
void add(int a, int b, int c)
{
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
int n, m;
int spfa(int u, int v)
{
memset(d, 0x3f, sizeof d);
queue<int> q;
d[u] = 0; q.push(u); st[u] = true;
while(q.size())
{
int t = q.front(); q.pop(); st[t] = false;
for(int i = h[t]; i != -1; i = ne[i])
{
int j = e[i];
if(d[j] > d[t] + w[i])
{
d[j] = d[t] + w[i];
if(!st[j]) q.push(j), st[j] = true;
}
}
}
return d[v] == 0x3f3f3f3f? -1: d[v];
}
int main()
{
cin>>n>>m;
memset(h, -1, sizeof h);
for(int i=1; i<=m; i++)
{
int u, v, w; cin>>u>>v>>w;
add(u, v, w);
}
int p = spfa(1, n);
if(p == -1) cout << "impossible";
else cout << p;
}
应用: 判断负环
#include <bits/stdc++.h>
using namespace std;
const int N = 2005, M = 10005;
int h[N], ne[M], e[M], w[M], idx;
int cnt[N], d[N];
bool st[N];
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
int n, m;
bool spfa()
{
queue<int> q;
for(int i=1; i<=n; i++) q.push(i), st[i] = true; //虚拟原点
while(q.size())
{
int t = q.front(); q.pop(); st[t] = false;
for(int i = h[t]; i != -1; i = ne[i])
{
int j = e[i];
if(d[j] > d[t] + w[i])
{
d[j] = d[t] + w[i];
cnt[j] = cnt[t] + 1;
if(cnt[j] > n) return true;
if(!st[j]) st[j] = true, q.push(j);
}
}
}
return false;
}
int main()
{
cin>>n>>m;
memset(h, -1, sizeof h);
for(int i=1; i<=m; i++)
{
int u, v, w; cin>>u>>v>>w;
add(u, v, w);
}
int p = spfa();
if(p) cout << "Yes";
else cout << "No";
}
Floyd
板子
#include <bits/stdc++.h>
using namespace std;
const int N = 205;
int g[N][N];
int n, m, k;
void Floyd()
{
for(int k = 1; k <= n; k++)
{
for(int i = 1; i<=n; i++)
{
for (int j = 1; j<=n; j++)
g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
}
}
}
int main()
{
cin>>n>>m>>k;
memset(g, 0x3f, sizeof g);
for(int i=1; i<=m; i++)
{
int u, v, w; cin>>u>>v>>w;
g[u][v] = min(g[u][v], w);
}
for(int i=1; i<=n; i++) g[i][i] = 0;
Floyd();
while(k--)
{
int u, v; cin>>u>>v;
if(g[u][v] > 0x3f3f3f3f/2) cout << "impossible"<<endl;
else cout << g[u][v] << endl;
}
}
最小生成树
Prim
朴素
#include <bits/stdc++.h>
using namespace std;
const int N = 505;
int g[N][N];
int n, m;
int d[N];
bool st[N];
int Prim()
{
int res = 0;
memset(d, 0x3f, sizeof d);
for(int i=0; i<n; i++)
{
int t = -1;
for(int j=1; j<=n; j++)
{
if(!st[j] && (t == -1 || d[t] > d[j])) t = j;
}
if(i && d[t] == 0x3f3f3f3f) return 0x3f3f3f3f;
if(i) res+=d[t];
for(int i=1; i<=n; i++)
d[i] = min(d[i], g[t][i]);
st[t] = true;
}
return res;
}
int main()
{
cin >> n>> m;
memset(g, 0x3f, sizeof g);
for(int i=1; i<=m; i++)
{
int u, v, w; cin>>u>>v>>w;
g[u][v] = g[v][u] = min(g[u][v], w);
}
for(int i=1; i<=n; i++) g[i][i] = 0;
int p = Prim();
if(p == 0x3f3f3f3f) cout << "impossible";
else cout << p;
}
堆优化
#include <bits/stdc++.h>
using namespace std;
const int N = 4e5+5, INF = 0x3f3f3f3f;
int h[N], e[N], ne[N], w[N], idx;
int n, m, res, tot;
int dist[N];
bool st[N];
typedef pair<int, int> PII;
void add(int a, int b, int c)
{
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
int Prim(int u) //从第u个顶点开始
{
priority_queue<PII, vector<PII>, greater<PII>> q;
memset(dist, 0x3f, sizeof dist);
dist[u] = 0; q.push({0, u});
while(q.size() && tot)
{
auto t = q.top(); q.pop();
int ver = t.second, distance = t.first;
if(st[ver]) continue;
st[ver] = true; tot--;
res += distance;
for(int i=h[ver]; i!=-1; i=ne[i])
{
int j=e[i];
if(!st[j] && dist[j] > w[i])
dist[j]=w[i], q.push({dist[j], j});
}
}
if(tot) return INF;
else return res;
}
int main()
{
cin>>n>>m;
memset(h, -1, sizeof h);
tot = n;
for(int i=1; i<=m; i++)
{
int u, v, w; cin>>u>>v>>w;
add(u, v, w); add(v, u, w);
}
int p = Prim(1);
if(p == INF) cout << "impossible";
else cout << p;
}
Kruskal
朴素
排序优化
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+5, M = 2 * N;
struct Edges{
int u;
int v;
int w;
friend bool operator < (Edges a, Edges b)
{
return a.w < b.w;
}
}edges[M];
int n, m;
int g[N];
int find(int x)
{
if(g[x] != x) g[x] = find(g[x]);
return g[x];
}
int main()
{
cin >> n >> m;
for(int i=1; i<=n; i++) g[i] = i;
for(int i=1; i<=m; i++)
{
int u, v, w; cin>>u>>v>>w;
edges[i] = {u, v, w};
}
sort(edges+1, edges+m+1);
int res = 0, tot = 0;
for(int i=1; i<=m; i++)
{
int u = edges[i].u, v = edges[i].v, w = edges[i].w;
u = find(u), v = find(v);
if(u != v)
{
g[u] = v;
res += w;
tot ++;
}
}
if(tot == n-1) cout << res;
else cout << "impossible";
}
二分图
染色法判定二分图
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+5, M = 2 * N;
int h[N], ne[M], e[M], idx;
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int n, m;
int color[N];
int dfs(int u, int c)
{
color[u] = c;
for(int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
if(!color[j])
{
if(!dfs(j, 3-c)) return false;
}
else if(color[j] == color[u]) return false;
}
return true;
}
int main()
{
cin >> n >> m;
memset(h, -1, sizeof h);
for(int i=1; i<=m; i++)
{
int u, v; cin>>u>>v;
add(u, v); add(v, u);
}
bool ok = true;
for(int i=1; i<=n; i++)
{
if(!color[i])
{
if(!dfs(i, 1))
{
ok = false;
break;
}
}
}
if(ok) cout << "Yes";
else cout << "No";
}
二分图的最大匹配
#include <bits/stdc++.h>
using namespace std;
const int N = 505, M = 1e5+5;
int h[N], ne[M], e[M], idx, match[N];
bool st[N];
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
bool find(int u)
{
for(int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
if(!st[j])
{
st[j] = true;
if(!match[j] || find(match[j]))
{
match[j] = u;
return true;
}
}
}
return false;
}
int n1, n2, m;
int main()
{
cin >> n1 >> n2 >> m;
memset(h, -1, sizeof h);
for(int i=1; i<=m; i++)
{
int u, v; cin>>u>>v;
add(u, v);
}
int res = 0;
for(int i=1; i<=n1; i++)
{
memset(st, false, sizeof st);
if(find(i)) res++;
}
cout << res;
}

浙公网安备 33010602011771号