图论板子

图论板子

1.最短路

(1)Dijkstra
无堆优化:

int n,m,G[N][N],d[N];
bool vis[N] = {false};
void Dij(int s){
    for(int i = 1;i <= n;i++) d[i] = inf;
    d[s] = 0;
    for(int i = 1;i <= n;i++){
        int x,_m = inf;
        for(int j = 1;j <= n;j++){//找到离起点距离最短并且没有找过的点
              if(!vis[j] && d[j] <= _m) _m = d[x=j];
        }
        vis[x] = true;//找到了最短路,并且更新一遍最短路树
        for(int j = 1;j <= n;j++)d[j] = min(d[j],d[x]+G[x][j]);
    }
}

堆优化:

ll n,m,s;
struct edge{
    ll to,w;
    edge(ll v,ll ww){to = v;w = ww;}
};
struct node{
    ll id,dis;
    node(ll x,ll y){id = x;dis = y;}
    bool operator < (const node &a)const
    {return dis > a.dis;}
};

vector<edge> e[N];
void add(ll u,ll v,ll w){
    e[u].push_back({v,w});
}
ll d[N];
bool done[N];
void dijkstra(){
    for(ll i = 1;i <= n;i++){
        d[i] = (i==s)?0:inf;
        done[i] = false;
    }
    priority_queue<node> q;
    q.push(node(s,d[s]));
    while(!q.empty()){
        node x = q.top();
        q.pop();
        if(done[x.id]) continue;
        done[x.id] = true;
        for(ll i = 0;i < e[x.id].size();i++){
            edge u = e[x.id][i];
            if(done[u.to])continue;
            if(d[u.to] > x.dis+u.w){
                d[u.to] = x.dis+u.w;
                q.push(node(u.to,d[u.to]));
            }
        }
    }
}

(2)bellman-ford

int n,m,s,d;
struct Edge{
    int u,v,w;
    Edge(int _u = 0,int _v = 0,int _w = 0):u(_u),v (_v),w(_w){}
};
vector<Edge> edge;
int dis[N];
void add(int u,int v,int w){
    edge.push_back(Edge(u,v,w));
}
bool bellman(){
    for(int i = 1;i <= n;i++) dis[i] = inf;
    dis[s] = 0;
    for(int i = 1;i <= n-1;i++){
        bool flag = false;
        for(int j = 0;j < edge.size();j++){
            int u = edge[j].u,v= edge[j].v,w = edge[j].w;
            if(dis[v] > dis[u]+w){
                dis[v] = dis[u]+w;
                flag = true;
            }
        }
        if(!flag) return true;
    }
    for(int i = 0;i < edge.size();i++)
        if(dis[edge[i].v] > dis[edge[i].u])
            return false;
    return true;
}

(3)Spfa

int n,m,s,t;//点数,边数,起点,终点
struct Edge{
    int u,v;
    int w;
    Edge(int _u = 0,int _v = 0,int _w = 0):u(_u),v(_v),w(_w){}
};
vector<Edge> G[N];
void add(int u,int v,int w){
    G[u].push_back(Edge(u,v,w));
}
bool vis[N];//检测是否在队列中
int cnt[N],dis[N];//cnt表示进入队列次数,dis表示距离
queue<int>q;
bool spfa(){
    memset(vis,false,sizeof(vis));
    for(int i = 1;i <= n;i++)dis[i] = inf;
    vis[s] = true;dis[s] = 0;
    while(!q.empty()) q.pop();
    q.push(s);
    memset(cnt,0,sizeof cnt);
    cnt[s] = 1;
    while(!q.empty()){
        int u = q.front();
        q.pop();vis[u] = false;
        for(auto e:G[u]){
            int v = e.v;
            if(dis[v] > dis[u]+e.w){
                dis[v] = dis[u]+e.w;
                if(!vis[v]){
                    vis[v] = true;
                    q.push(v);
                    if(++cnt[v] > n)return false;
                    //因为存在一个点被访问了超过n次所以该回路存在负环回路
                }
            }
        }
    }
    return true;
}

2.最小生成树

(1) Kruskal算法(和并查集的板子一起了)


ll n,m,cnt = 0;
struct Edge{
    ll u,v,w;
    bool operator < (const Edge &x)const{return x.w > w;}
}edge[N];
ll f[N] = {0};
ll find(ll x){
    return x == f[x]?x:f[x] = find(f[x]);
}
void _union(ll x,ll y){
    x = find(x),y = find(y);
    if(x != y) f[x] = y;
}
ll ans = 0;
void Kruskal(){
    for(int i = 1;i <= n;i++) f[i] = i;
    sort(edge+1,edge+m+1);
    ll ans = 0;
    for(int i = 1;i <= m;i++){
         ll u = edge[i].u,v = edge[i].v;
         if(find(u) != find(v)){
             ans += edge[i].w;
             _union(u,v);
         }
    }
}

(2)prim算法
//dij改一下就是了


bool vis[N];
ll lowc[N],n;
void prim(){
    memset(vis,false,sizeof vis);
    ll ans = 0;
    vis[1] = true;
    for(ll i = 2;i <= n;i++) lowc[i] = G[1][i];
    for(ll i = 2;i <= n;i++){
        ll minc = inf;
        ll tmp = -1;
        for(ll j = 1;j <= n;j++){
            if(!vis[j] && minc > lowc[j]){
                minc = lowc[j];
                tmp = j;
            }
        }
        ans += minc;
        vis[tmp] = true;
        for(ll j = 1;j <= n;j++){
            if(!vis[j] && lowc[j] > G[tmp][j]){
                lowc[j] = G[tmp][j];
            }
        }
    }
    cout<<ans<<endl;
}

3.联通性

tarjan

int n,m;  
struct Edge{  
    ll to,nxt;  
}edge[N];  
int head[N],tot;  
int low[N],dfn[N],sta[N],co[N];  
bool is_sta[N] = {false};  
int cnt = 0,top = 0;  
int scc = 0;//连通分量的个数  
int num[N];//每个强连通分量的点数  
void add(int u,int v){  
    edge[tot].to = v;  
    edge[tot].nxt = head[u];  
    head[u] = tot++;  
}  
void tarjan(int u){  
    int v;  
    low[u] = dfn[u] = ++cnt;  
    sta[top++] = u;  
    is_sta[u] = true;  
    for(int i = head[u];~i;i = edge[i].nxt){  
        v = edge[i].to;  
	if(!dfn[v]){  
            tarjan(v);  
	    low[u] = min(low[u],low[v]);  
        }  
        else if(is_sta[v]) low[u] = min(low[u],dfn[v]);  
    }  
    if(low[u] == dfn[u]){  
        scc++;  
	do{  
            v = sta[--top];  
	    is_sta[v] = false;  
	    co[v] = scc;  
	    num[scc]++;  
	}while(v != u);  
  }  
}  
void solve(){  
    memset(dfn,0,sizeof(dfn));  
    memset(is_sta,false,sizeof(is_sta));  
    memset(num,0,sizeof(num));  
    cnt = top = scc = 0;  
    for(int i = 1;i <= n;i++){  
        if (!dfn[i])tarjan(i);  
    }  
}  
void init(){  
    tot = 0;  
    memset(head,-1,sizeof(head));  
}  

(2)Kosaraju

struct Edge{
    int to,next;
}edge1[N],edge2[N];
int head1[N],head2[N];
bool ok1[N],ok2[N];
int tot1,tot2;
int cnt1,cnt2;
int st[N],be[N],scc[N];//对原图dfs,标记时间;记录每一个点在哪个连通分量;记录某一连通分量的点数
int tmp = 0;//中间变量
void add(int u,int v){
    edge1[tot1].to = v;edge1[tot1].next = head1[u];head1[u] = tot1++;//建立正图
    edge2[tot2].to = u;edge2[tot2].next = head2[v];head2[v] = tot2++;//建立反图
}
void dfs1(int u){
    ok1[u] = true;
    for(int i = head1[u];~i;i = edge1[i].next){
        int v = edge1[i].to;
        if(!ok1[v]) dfs1(v);
    }
    st[cnt1++] = u;
}
void dfs2(int u){
    ok2[u] = true;
    tmp++;
    be[u] = cnt2;
    for(int i = head2[u];~i;i = edge2[i].next){
        int v = edge2[i].to;
        if(!ok2[v]) dfs2(v);
    }
}
void slove(){
    memset(ok1,false,sizeof ok1);
    memset(ok2,false,sizeof ok2);
    cnt1 = cnt2 = 0;
    for(int i = 1;i <= n;i++){
        if(!ok1[i]) dfs1(i);
    }
    for(int i = cnt1-1;i >= 0;i--){
        if(!ok2[st[i]]){
            tmp = 0;
            dfs2(st[i]);
            scc[cnt2++] = tmp;
        }
    }
}

4.二分图

(1)匈牙利算法

int m,n;				     //左右的集合的元素数量  
int G[N][N];				     //邻接矩阵存图  
int p[N] = {0};				     //右侧元素对应的左侧元素  
bool vis[N];				     //记录右侧元素是否被访问过  
  
bool match(int i){  
    for(int j = 1;j <= n;j++){  
        if(G[i][j] && !vis[j]){              //有边且未访问  
              vis[j] = true;                 //记录状态被访问过  
	      if(p[j] == 0 || match(p[j])){  //暂无匹配,原匹配的左侧元素可以找到新元素  
		  p[j] = i;                  //当前左侧元素成为当前右侧元素的新匹配  
		  return true; 	             //返回匹配成功  
	      }  
        }  
    }  
    return false; 			      //循环结束,未找到匹配,返回匹配失败  
}  
int Hungarian(){  
    int cnt = 0;  
	for(int i = 1;i <= m;i++){  
        memset(vis,0,sizeof vis);             //重置vis数组  
		if(match(i)) cnt++;  
	}  
    return cnt;  
}  

网络流Dinic


struct Edge{ll to,w,nxt;}edge[M];
ll head[N],cnt = 1;
void add(ll u,ll v,ll w){
    edge[++cnt] = {v,w,head[u]};
    head[u] = cnt;
}
void add2(ll u,ll v,ll w){
    add(u,v,w);
    add(v,u,0);
}
ll s,t,lv[N],cur[N];
bool bfs(){
    memset(lv,-1,sizeof lv);
    lv[s] = 0;
    memcpy(cur, head, sizeof(head));
    queue<ll>q;q.push(s);
    while(!q.empty()){
        ll p = q.front();q.pop();
        for(ll eg = head[p];eg;eg = edge[eg].nxt){
            ll to = edge[eg].to,vol = edge[eg].w;
            if(vol > 0 && lv[to] == -1) lv[to] = lv[p] +1,q.push(to);
        }
    }
    return lv[t] != -1;
}
ll dfs(ll p = s,ll flow = inf){
    if(p == t)return flow;
    ll rmn = flow;
    for(ll &eg = cur[p];eg;eg = edge[eg].nxt){
        if(!rmn)break;
        ll to = edge[eg].to,vol = edge[eg].w;
        if(vol > 0 && lv[to] == lv[p]+1){
            ll c = dfs(to,min(vol,rmn));
            rmn -= c;
            edge[eg].w -= c;
            edge[eg^1].w += c;

            if(rmn == 0)break;
        }
    }
    return flow-rmn;
}
ll dinic(){
    ll ans = 0;
    while(bfs()) ans += dfs();
    return ans;
}

费用流

#include <iostream>
#include <queue>
#define ll long long
const ll N = 5e3+50,M = 5e4+50;
const ll inf = 0x3f3f3f3f;
using namespace std;
ll head[N],cnt = 1;
//将EK的bfs变为spfa
struct Edge{
    ll to,w,cost,nxt;
}edge[M*2];
void add(ll u,ll v,ll w,ll c){
    edge[++cnt] = {v,w,c,head[u]};
    head[u] = cnt;
}
void  add2(ll u,ll v,ll w,ll cost){
    add(u,v,w,cost);
    add(v,u,0,-cost);
}
ll s,t,dis[N],cur[N];
bool inq[N],vis[N];
queue<ll>Q;
bool spfa(){
    while(!Q.empty()) Q.pop();
    copy(head,head+N,cur);
    fill(dis,dis+N,inf);
    dis[s] = 0;
    Q.push(s);
    while(!Q.empty()){
        ll p = Q.front();
        Q.pop();
        inq[p] = false;
        for(ll e = head[p];e;e = edge[e].nxt){
            ll to = edge[e].to,vol = edge[e].w;
            if(vol > 0 && dis[to]>dis[p]+edge[e].cost){
                dis[to] = dis[p] + edge[e].cost;
                if(!inq[to]){
                    Q.push(to);
                    inq[to] = true;
                }
            }
        }
    }
    return dis[t] != inf;
}
ll dfs(ll p = s,ll flow = inf){
    if(p == t) return flow;
    vis[p] = true;
    ll rmn = flow;
    for(ll eg = cur[p];eg && rmn;eg = edge[eg].nxt){
        cur[p] = eg;
        ll to = edge[eg].to,vol = edge[eg].w;
        if(vol > 0 && !vis[to]&&dis[to] == dis[p]+edge[eg].cost){
            ll c = dfs(to,min(vol,rmn));
            rmn -= c;
            edge[eg].w -= c;
            edge[eg^1].w += c;
            if(rmn == 0)break;`
        }
    }
    vis[p] = false;
    return flow-rmn;
}
ll maxflow = 0,mincost = 0;
void dinic(){
    while(spfa()){
        ll flow = dfs();
        maxflow += flow;
        mincost += dis[t]*flow;
    }
}
int main() {
    ios::sync_with_stdio(false);
    ll n,m;
    cin>>n>>m>>s>>t;
    while(m--){
        ll u,v,w,c;cin>>u>>v>>w>>c;
        add2(u,v,w,c);
    }
    dinic();
    cout<<maxflow<<' '<<mincost<<endl;
    return 0;
}

LCA

(1)树上倍增

struct Edge{
    ll to,nxt;
}edge[2*N];
ll head[2*N],tot;
void init(){
    for(int i=0;i<2*N;++i){
        edge[i].nxt = -1;
        head[i] = -1;
    }
    tot = 0;
}
void add(ll u,ll v){
    edge[tot].to = v;
    edge[tot].nxt = head[u];
    head[u] = tot++;
}
ll fa[N][20],deep[N];
//fa[x][i]表示,x的第2^i个祖先,fa[x][i] = fa[fa[x][i-1]][i-1]
//deep[x]是x的深度
void dfs(ll u,ll father){//求u的深度
    deep[u] = deep[father]+1;
    fa[u][0] = father;
    for(ll i = 1;(1<<i)<=deep[u];i++){
        fa[u][i] = fa[fa[u][i-1]][i-1];
    }
    for(ll i = head[u];~i;i = edge[i].nxt){
        if(edge[i].to != father){
            dfs(edge[i].to,u);
        }
    }
}
ll LCA(ll x,ll y){
    if(deep[x] < deep[y]) swap(x,y);
    for(ll i = 19;i >= 0;i--){//先让x跳到y的层数
        if(deep[x]-(1<<i) >= deep[y]) x = fa[x][i];
    }
    if(x == y) return x;
    for(ll i = 19;i >= 0;i--){//让x,y一起跳,最多只能跳到LCA的下一层
        if(fa[x][i] != fa[y][i]){
            x = fa[x][i];
            y = fa[y][i];
        }
    }
    return fa[x][0];
} 
posted @ 2021-03-09 19:15  Paranoid5  阅读(129)  评论(0编辑  收藏  举报