图论总结模板

图论

  • 图论
    • 最短路
      • Dijkstra
      • 次短路
      • Floyd
      • Bellman-Ford-SPFA
    • 二分图匹配
      • 无权图匹配匈牙利算法 Onn
      • HK On 12 m
      • 带权图匹配KM Ommn
    • 最大团
    • 网络流
      • 最大流
        • 简单增广路 nm2nm2
        • Dinic mn2mn2
        • ISPA
      • 费用流

DAG : Directed Acyclic Graph 无回路有向图

最短路

Dijkstra

你一直困惑的原因是你忽略了dijkstra算法的思想每次利用优先队列找出d值最小的节点然后从该节点出发更新该节点边上的节点这样的话 如果d值小于p.first 说明之前的操作中已经该节点已经被更新,而且更新之后的节点存在于后面的队列中 故此处无需考虑 如果d值不小于p.first 说明d值等于p.fist 即表明之前的操作中没有被更新,故此处可以进行对与其相邻元素的更新工作

struct edge {int to, cost; };
typedef pair<int , int> P;///first 是最短距离 second 是顶点编号
vector G[maxn];
int d[maxn];///到某个顶点的最短距离
void dijkstra(int s)
{
    priority_queuevector

, greater

>que; fill(d, d+N, INF); d[s] = 0; que.push(P(0 , s)); while(!que.empty()) { P p = que.top(); que.pop(); int v = p.second; if(d[v] < p.first) continue;///否则就要更新与p.second接壤的节点 for(int i=0; iif(d[e.to] > d[v] + e.cost) { d[e.to] = d[v] + e.cost; que.push(P(d[e.to], e.to)); } } } }

次短路

struct edge {int to, cost; };
typedef pair<int,int> P;
int V;
vector G[maxn];
int d[maxn];//最短路
int d2[maxn];//次短路
void dijkstra(int s)
{
    priority_queuevector

,greater

>q; for(int i=0; i0; q.push(P(d[s],s)); while(!q.empty()) { P p = q.top(); q.pop(); int v = p.second,c=p.first; if(d2[v]continue; for(int i=0; iint c2 = c+e.w;//新的权值 if(d[e.to]>c2) //维护最短路 { swap(d[e.to],c2); q.push(P(d[e.to],e.to)); } if(d2[e.to]>c2&&d[e.to]//维护次短路 { d2[e.to]=c2; q.push(P(d2[e.to],e.to)); } } } }

Floyd

int n;
int d[maxn][maxn];

void Floyd()
{
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
        d[i][j] = (i==j ? 0 : INF);

    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]);
}

Bellman-Ford—-SPFA

struct Edge
{
    int v, w, next;
}edge[maxn*20]//边数根据具体情况而定

int n, m, tot;
int dist[maxn], head[maxn];
bool vis[maxn];

void add_edge(int u, int v, int w)
{
    edge[tot].v = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}

void  SPFA(int s)
{
    memset(vis, false, (n+2)*sizeof(bool));
    for(int i=1; i<=n; i++)  dist[i] = INF;

    vis[s] = true;
    dist[s] = 0;
    queue<int> Q;
    Q.push(s);
    while(!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        vis[u] = false;//重要点一
        for(int j=head[u]; j!=-1; j=edge[j].next)
        {
            int v = edge[j].v;
            int w = edge[j].w;
            if(dist[u] < INF && dist[u] + w < dist[v])
            {
                dist[v] = dist[u] + w;
                if(!vis[v])
                {
                    vis[v] = true;//重要点二
                    Q.push(v);
                }
            }
        }
    }
}

二分图匹配

无权图匹配(匈牙利算法) O(n*n)

#include 

using namespace std;
#define maxn 1000

vector<int>G[maxn];
bool vis[maxn];
int link[maxn];
int n, m;

bool Find(int u)
{
    for(int i=0; iint &v = G[u][i];
        if(!vis[v])
        {
            vis[v] = true;
            if(link[v]==-1 || Find(link[v]))
            {
                link[v] = u;
                link[u] = v;//这个地方要注意,无向图,区别于两组对象
                return true;
            }
        }
    }
    return false;
}

void solve()
{

    int ans = 0;
    for(int i=1; i<=n; i++)
    if(link[i]==-1)///这里需要加上判断 否则对已经匹配好的进行重新匹配,造成错误结果
    {
        memset(vis, 0, sizeof(vis));
        if(Find(i))  ans++;
    }
    printf("%d\n", ans);
}

HK O((n ^ 1/2) * m )

带权图匹配(KM) (O(m*m*n))

const int N = 310;
const int INF = 0x3f3f3f3f;
int nx,ny;//两边的点数
int g[N][N];//二分图描述  坐标点为 0 ~ n - 1
int linker[N],lx[N],ly[N];//y中各点匹配状态,x,y中的点标号
int slack[N];
bool visx[N],visy[N];
bool DFS(int x)
{
    visx[x] = true;
    for(int y = 0; y < ny; y++)
    {
        if(visy[y])continue;
        int tmp = lx[x] + ly[y] - g[x][y];
        if(tmp == 0)
        {
            visy[y] = true;
            if(linker[y] == -1 || DFS(linker[y]))
            {
                linker[y] = x;
                return true;
            }
        }
        else if(slack[y] > tmp)
            slack[y] = tmp;
    }
    return false;
}
int KM()
{
    memset(linker,-1,sizeof(linker));
    memset(ly,0,sizeof(ly));
    for(int i = 0; i < nx; i++)
    {
        lx[i] = -INF;
        for(int j = 0; j < ny; j++)
            if(g[i][j] > lx[i])
                lx[i] = g[i][j];
    }
    for(int x = 0; x < nx; x++)
    {
        for(int i = 0; i < ny; i++)
            slack[i] = INF;
        while(true)
        {
            memset(visx,false,sizeof(visx));
            memset(visy,false,sizeof(visy));
            if(DFS(x))break;
            int d = INF;
            for(int i = 0; i < ny; i++)
                if(!visy[i] && d > slack[i])
                    d = slack[i];
            for(int i = 0; i < nx; i++)
                if(visx[i])
                    lx[i] -= d;
            for(int i = 0; i < ny; i++)
            {
                if(visy[i])ly[i] += d;
                else slack[i] -= d;
            }
        }
    }
    int res = 0;
    for(int i = 0; i < ny; i++)
        if(linker[i] != -1)
            res += g[linker[i]][i];
    return res;
}

最大团

详细讲解

网络流

最大流

原理:在残量网络上不断贪心的进行增广(最小割最大流)

1. 简单增广路 ( nm2 )

struct Edge
{
    int from, to, cap, flow;
    Edge(int u, int v, int c, int f):from(u), to(v), cap(c), flow(f){}
};

int n, m;
vector edges;
vector<int> G[maxn];
int a[maxn];
int p[maxn];

void init()
{
    for(int i=0; ivoid AddEdge(int from, int to, int cap)
{
    edges.push_back(Edge(from, to, cap, 0));
    edges.push_back(Edge(to, from, 0, 0));
    m = edges.size();
    G[from].push_back(m-2);
    G[to].push_back(m-1);
}

int Maxflow(int s, int t)
{
    int flow = 0;
    for(;;)
    {
        memset(a, 0, sizeof(a));
        queue<int> Q;
        Q.push(s);
        a[s] = INF;
        while(!Q.empty())
        {
            int x = Q.front(); Q.pop();
            for(int i=0; iif(!a[e.to] && e.cap > e.flow)
                {
                    p[e.to] = G[x][i];
                    a[e.to] = min(a[x], e.cap - e.flow);
                    Q.push(e.to);
                }
            }
            if(a[t]) break;
        }
        if(!a[t]) break;
        for(int u=t; u!=s; u=edges[p[u]].from)
        {
            edges[p[u]].flow += a[t];
            edges[p[u]^1].flow -= a[t];
        }
        flow += a[t];
    }
    return flow;
}

2. Dinic ( mn2 )

  • 分层图
  • 阻塞流

测试题 HDU 1532

struct Edge
{
    int u, v, c, next;
}e[E<<2];

int n, m, s, t;
int d[V], head[V], cnt;//注意head在主函数的需要初始化

void adde(int u, int v, int c)//添加一条有向边,如果是无向边需添加两次
{
    e[cnt].u = u;
    e[cnt].v = v;
    e[cnt].c = c;
    e[cnt].next = head[u];
    head[u] = cnt++;

    e[cnt].u = v;
    e[cnt].v = u;
    e[cnt].c = 0;
    e[cnt].next = head[v];
    head[v] = cnt++;
}

int bfs()//构造层次图
{
    memset(d, -1, (n+3)*sizeof(int));
    d[s] = 0;
    queue<int> Q;
    Q.push(s);
    while(!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        for(int i=head[u]; i!=-1; i=e[i].next)
        {
            int v = e[i].v;
            if(d[v]==-1 && e[i].c>0)
               {
                   d[v] = d[u] + 1;
                   Q.push(v);
               }
        }
    }
    return d[t] != -1;
}

int dfs(int x, int a)//寻找最短增广路
{
    if(x==t || a==0) return a;
    int flow = 0, f;
    for(int i=head[x]; i!=-1 && flowint v = e[i].v;
        if(d[v]==d[x]+1 && e[i].c>0)
        {
            int t = min(e[i].c, a-flow);
            f = dfs(v, t);
            flow += f;
            e[i].c -= f;
            e[i^1].c += f;
        }
    }
    if(!flow)  d[x] = -2;
    return flow;
}

int dinic(int s, int t)//不断进行构造层次图,然后寻找最短增广路,找出最大流
{
    int flow = 0, f;
    while(bfs())
    {
        while(f = dfs(s, INF))
        flow += f;
    }
    return flow;
}

3. ISPA

费用流

原理:在残量网络上总是沿着最短路进行增广(将费用当做距离来进行考虑)

如果要考虑流量f固定之下的最小费用,可以加一条容量为f从源点出发的边
#define MOD 0x3f3f3f3f
#define V 1000 + 10  //边数
#define E 10000 + 10 //点数
int n, m, flow_sum;// 点数 边数 总流量
bool vis[V];
int cnt, dist[V], head[V], pre[V];
struct Edge
{
    int u, v, c, cost, next;//This 'c' means capacity
} edge[E<<2];
void init()
{
    cnt = flow_sum = 0;
    memset(head, -1, (n+3) * sizeof(int));
}
void adde(int u, int v, int c, int cost)
{
    edge[cnt].u = u;
    edge[cnt].v = v;
    edge[cnt].c = c;
    edge[cnt].cost = cost;
    edge[cnt].next = head[u];
    head[u] = cnt++;
    edge[cnt].u = v;
    edge[cnt].v = u;
    edge[cnt].c = 0;
    edge[cnt].cost = -cost;
    edge[cnt].next = head[v];
    head[v] = cnt++;
}
bool spfa(int begin, int end)
{
    int u, v;
    queue<int> q;
    memset(pre, -1, (n+3)*sizeof(int));
    memset(vis, 0, (n+3)*sizeof(bool));
    memset(dist, 0x3f, (n+3)*sizeof(int));
    vis[begin] = 1;
    dist[begin] = 0;
    q.push(begin);
    while(!q.empty())
    {
        u = q.front();
        q.pop();
        vis[u] = false;
        for(int i=head[u]; i!=-1; i=edge[i].next)
            if(edge[i].c > 0)
            {
                v = edge[i].v;
                if(dist[v] > dist[u] + edge[i].cost)
                {
                    dist[v] = dist[u] + edge[i].cost;
                    pre[v] = i;
                    if(!vis[v])
                    {
                        vis[v] = true;
                        q.push(v);
                    }
                }
            }
    }
    return dist[end] != MOD;
}

int MCMF(int begin, int end)
{
    int ans = 0, flow;
    while(spfa(begin, end))
    {
        flow = MOD;
        for(int i=pre[end]; i!=-1; i=pre[edge[i].u])
            flow = min(flow, edge[i].c);
        for(int i=pre[end]; i!=-1; i=pre[edge[i].u])
        {
            edge[i].c -= flow;
            edge[i^1].c += flow;
        }
        ans += dist[end] * flow;
        flow_sum += flow;
    }
    return ans;//返回最小费用
}

你可能感兴趣的:(Summary)