搜索与图论

搜索与图论

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;
}
posted @ 2021-03-01 15:39  2wx  阅读(54)  评论(0)    收藏  举报