zoukankan      html  css  js  c++  java
  • 图论

    拓扑排序

    • 一般不会考裸题,会判环或配合(dp)

    • (bfs)

    bool topo() {
    	queue <int> q; int cnt = 0;
    	for(int i = 1;i <= n; ++i) if(!ind[i]) q.push(i);
    	while(!q.empty()) {
    		int u = q.front(); q.pop(); ++cnt;
    		for(int i = head[u]; i; i = e[i].nxt) {
    			int v = G[u][i];
    			--ind[v];
    			if(!ind[v]) q.push(v);
    		}
    	}
    	return cnt == n;// == topo成功, != 则失败
    }
    

    最小生成树

    • 最小生成树有许多变式,此处先只讨论最简单的

    (Kruskal)(贪边)

    • 适用于稀疏图
    struct edge {
        int u,v,w;
        bool operator < (const edge &sed) const {
            return w < sed.w;
        }
    }e[M];
    
    int find(int x) {
        return fa[x] == x ? x : fa[x] = find(fa[x]);
    }
    
    int main() {
        int i; in(n); in(m);
        for(i = 1;i <= m; ++i) {
            in(e[i].u); in(e[i].v); in(e[i].w);
        }
        sort(e+1,e+m+1);
        for(i = 1;i <= n; ++i) fa[i] = i;
        int cnt = 0;
        for(i = 1;i <= m; ++i) {
            int x = find(e[i].u),y = find(e[i].v);
            if(x == y) continue;
            ++cnt; fa[x] = y; ans += e[i].w;
            if(cnt == n-1) break;
        }
        if(cnt == n-1) out(ans);
        else printf("orz");
        return 0;
    }
    

    (Prim)(贪点)

    • (n^2)算法适用于稠密图(特别是类似二维平面的所有点之间都有边的完全图)
    for(k = 1;k <= n; ++k) {//枚举轮数(n个点,每轮标记1个,所以有n轮)
        for(u = 0,i = 1;i <= n; ++i) if(!vis[i] && dis[i] < dis[u]) u = i;
    	//找到未被标记的dis值最小的点
        for(v = 1;v <= n; ++v) if(!vis[v] && maps[u][v] < dis[v]) dis[v] = maps[pr[v] = u][v];
    	//更新 
    }
    
    • 思想和算法框架与求最短路的(Dijkstra)几乎一致
    struct node {
        int pos,dis;
        node(int pos = 0,int dis = 0):pos(pos),dis(dis){};
        bool operator < (const node &sed) const {
            return dis > sed.dis;//debug 优先队列默认是大根堆
        }
    };
    
    priority_queue <node> q;
    
    void prim() {//贪点 
        memset(dis,0x3f,sizeof(dis));
        q.push(node(1,0)); dis[1] = 0; int cnt = 0;//debug 此处cnt为已加入树中的点数(不同于kruskal的贪边) 
        while(!q.empty()) {
            node _u = q.top(); q.pop();
            int u = _u.pos;
            if(vis[u]) continue; vis[u] = 1;
            ++cnt; ans += _u.dis;
            if(cnt == n) break;//注意 
            for(int i = head[u]; i;i = e[i].nxt) {
                int v = e[i].v;
                if(dis[v] > e[i].w) {
                    dis[v] = e[i].w; q.push(node(v,dis[v]));
                }
            }
        }
        if(cnt == n) out(ans);
        else printf("orz");
    }//prim 的写法与dijkstra如出一辙 主要区别在于dijkstra的dis代表到源点的最短距离,而prim的dis代表未加入树集的点到树集(连通块)的最小距离 
    //本质上都是贪心
    

    最短路

    • 单源最短路

    (SPFA)((SLF)优化)

    void spfa() {
        memset(dis,0x3f,sizeof(dis));
        q.push_back(s); dis[s] = 0; inque[s] = 1;
        while(!q.empty()) {
            int u = q.front(); q.pop_front(); inque[u] = 0;//易错未写 inque[u] = 0 
            for(int i = head[u]; i;i = e[i].nxt) {
                int v = e[i].v;
                if(dis[v] > dis[u]+e[i].w) {
                    dis[v] = dis[u]+e[i].w;
                    if(!inque[v]) {
                        if(!q.empty() && dis[v] < dis[q.front()]) q.push_front(v);//注意 易错未写 !q.empty()
                        else q.push_back(v);
                        inque[v] = 1;
                    }
                }
            }
        }
    }
    

    (Dijkstra)(堆优化)

    struct node {
        int pos; ll dis;
        node(int pos = 0,ll dis = 0):pos(pos),dis(dis){};
        bool operator < (const node &sed) const {
            return dis > sed.dis;//debug 又写反了 
        }
    };
    
    priority_queue <node> q;
    
    void dijkstra() {
        memset(dis,0x7f,sizeof(dis));
        q.push(node(s,0)); dis[s] = 0; //vis[s] = 1;
        while(!q.empty()) {
            node _u = q.top(); q.pop();
            int u = _u.pos;
            if(vis[u]) continue; vis[u] = 1;
            for(int i = head[u]; i;i = e[i].nxt) {
                int v = e[i].v; 
                if(dis[v] > dis[u]+e[i].w) {
                    dis[v] = dis[u]+e[i].w;
                    //if(vis[v]) continue;
                    q.push(node(v,dis[v]));
                    //vis[v] = 1;
                }
            }
        }
    }
    

    (Floyd)

    多源最短路算法,时间复杂度 (O(n^3))

    • 基础应用

    (floyd)的本质是(dp),设(f[k,i,j])表示只经过前(k)个节点从(i)(j)的最短路长度

    [f[k,i,j] = min(f[k-1,i,j],f[k-1,i,k]+f[k-1,k,j]) ]

    即对于每个点对((i,j)),每个点只有两种决策——被经过或不被经过

    (第一维可省略)

    即有

    [f[i,j] = min(f[i,j],f[i,k]+f[k,j]) ]

    • 传递闭包

    [f[i,j] ;|= f[i,k]&f[k,j] ]

    • 求最小环
    1. (dijkstra)

      枚举每条边,求删去这条边后此边所连两点的最短路,即做(m)(dijkstra)

    2. (floyd)

      设已知(k,i,j),其中(k)(i,j)邻接,此时求最小环只需求得(i,j)不经过(k)的最短路(答案就是这个再加上两个边权).

      考虑(floyd)的过程,枚举(k)的时候(f[i,j])表示是只考虑前(k-1)个点的最短路,的确没有经过(k),但问题是最小环可能还包含大于(k)的点

      考虑增加限制,每次求(k)为最大点的最小环,最小环一定有一个最大点(k'),(k')一定会被枚举到,所以这样也一定可以求得答案

      我们可以在每次转移前顺便求得(k)为最大点的最小环

    • 代码
    for(int k = 1;k <= n; ++k) {
    	for(int i = 1;i < k; ++i) {
    		for(int j = i+1;j < k; ++j) {//对称的点对(i,j)和(j,i)是等价的(无向图),所以不妨设i<j
    			ans = min(ans,f[i][j]+a[j][k]+a[k][i]);
    		}
    	}
    	for(int i = 1;i <= n; ++i) {
    		for(int j = 1;j <= n; ++j) {
    			f[i][j] = min(f[i][j],f[i][k]+f[k][j]);
    		}
    	}
    }
    
    • 输出方案
    vector <int> path;
    
    void get_path(int x,int y) {
    	if(!g[x][y]) return;
    	get_path(x,g[x][y]);
    	path.push_back(g[x][y]);
    	get_path(g[x][y],y);
    }
    
    memset(a,0x3f,sizeof(a));
    memset(dis,0x3f,sizeof(dis));
    for(i = 1;i <= n; ++i) a[i][i] = 0;
    for(i = 1;i <= m; ++i) {
    	in(u); in(v); in(w);
    	dis[u][v] = dis[v][u] = a[u][v] = a[v][u] = min(a[u][v],w);
    }
    for(k = 1;k <= n; ++k) {
      	for(i = 1;i < k; ++i) {
    		for(j = i+1;j < k; ++j) {
    			ll tmp = dis[i][j]+a[j][k]+a[k][i];
    			if(ans > tmp) {
    				ans = tmp;
    				path.clear();
    				path.push_back(i); get_path(i,j);
    				path.push_back(j); path.push_back(k);
    			}
    		}
    	}
    	for(i = 1;i <= n; ++i) {
    		for(j = 1;j <= n; ++j) {
    			ll tmp = dis[i][k]+dis[k][j];
    			if(dis[i][j] > tmp) {
    				dis[i][j] = tmp;
    				g[i][j] = k;
    			}
    		}
    	}
    }
    if(ans == inf) printf("No solution.");
    else {
    	for(i = 0;i < path.size(); ++i) {
    		out(path[i]); putchar(' ');
    	}
    }
    
  • 相关阅读:
    今天,你ak了吗?①
    线段树模板
    DP(关于字符串,数字串的)
    Leedsdiscussion
    高数积分求旋转体体积
    tiny mission
    莫队+数组低级化的 优先队列
    LAB2
    Leedslecturepronouncation
    Eclipse Access Restriction
  • 原文地址:https://www.cnblogs.com/mzg1805/p/11544814.html
Copyright © 2011-2022 走看看