• 图论二


    0、目录

    最短路、最小生成树、LCA
    (参考自白皮)

    1、最短路

    1.1、Floyd

    for(int i=1;i<=n;i++){
    	for(int j=1;j<=n;j++){
    		mat[i][j]=INF;
    		if(i==j) mat[i][j]=0;
    		pre[i][j]=i;		
    	}
    }
    
    for(int k=1;k<=n;k++){
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=n;j++){
    			dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
    		}
    	}
    }
    
    vector<int> path;
    path.push_back(t);
    while(pre[s][t]!=s) {
    	path.pb(pre[s][t]);
    	t=pre[s][t];
    }
    path.push_back(s);
    reverse(path.begin(),path.end());
    

    1.2、Dijkstra

    struct Edge{
    	int u,v,d;
    };
    
    struct HeapNode{
    	int d,u;
    	HeapNode(int d,int u):d(d),u(u){}
    	bool operator < (const HeapNode& tmp) const {
    		return d>tmp.d;
    	}
    };
    
    struct Dijkstra{
    	int n,m;
    	vector<Edge> egs;
    	vector<int> G[maxn];
    	bool done[maxn];
    	int d[maxn];
    	int p[maxn];
    	
    	void init(int n){
    		this->n=n;
    		for(int i=0;i<n;i++) G[i].clear();
    		egs.clear(); 
    	}
    	
    	void addEdge(int u,int v,int d){
    		egs.push_back(Edge(u,v,d));
    		m=egs.size();
    		G[u].push_back(m-1);
    	}
    	
    	void dijkstra(int s){
    		priority_queue<HeapNode> Q;
    		for(int i=0;i<n;i++) d[i]=INF;
    		d[s]=0;
    		memset(done,0,sizeof(done));
    		Q.push(HeapNode(0,s));
    		while(!Q.empty()){
    			HeapNode x=Q.top(); Q.pop();
    			int u=x.u;
    			if(done[u]) continue;
    			done[u]=1;
    			for(int i=0;i<G[u].size();i++){
    				Edge& e=egs[G[u][i]];
    				if(d[e.v]>d[u]+e.d){
    					d[e.v]=d[u]+e.d;
    					p[e.v]=G[u][i];
    					Q.push(HeapNode(d[e.v],e.v));
    				}
    			}
    		}
    	}
    };
    

    1.3、Spfa

    struct Spfa{
    	int n,m;
    	vector<Edge> egs;
    	vector<int> G[maxn];
    	bool inq[maxn];
    	int d[maxn];
    	int p[maxn];
    	int cnt[maxn];
    	
    	void init(int n){
    		this->n=n;
    		for(int i=0;i<n;i++) G[i].clear();
    		egs.clear(); 
    	}
    	
    	void addEdge(int u,int v,int d){
    		egs.push_back(Edge(u,v,d));
    		m=egs.size();
    		G[u].push_back(m-1);
    	}
    	
    	bool spfa(int s){
    		queue<int> Q;
    		memset(inq,0,sizeof(inq));
    		memset(cnt,0,sizeof(cnt));
    		for(int i=0;i<n;i++) d[i]=INF;
    		d[s]=0,inq[s]=true,Q.push(s);
    		while(!Q.empty()){
    			int u=Q.front(); Q.pop();
    			inq[u]=false;
    			for(int i=0;i<G[u].size();i++){
    				Edge& e=egs[G[u][i]];
    				if(d[e.v]>d[u]+e.d){
    					d[e.v]=d[u]+e.d;
    					p[e.v]=G[u][i];
    					if(!inq[e.v]){
    						Q.push(e.v),inq[e.v]=true;
    						if(++cnt[e.v]>n) return false;
    					}
    				}
    			}
    		}
    		return true;
    	}
    };
    

    2、最小生成树

    2.0、两个性质

    • 切割性质:假定所有的边权均不相等。设S为既非空集也非全集的V的子集,边e是满足一个端点在S内,另一个端点不在S内的所有边中权值最小的一个,则图G的所有最小生成树均包含e。
    • 回路性质:假定所有的边权均不相同。设C是图G中的任意回路,边e是C上权值最大的边,则图G的所有最小生成树均不包含e。

    2.1、无相图

    /*kruskal*/
    int fa[maxn];
    int find(int x){ return fa[x]=fa[x]==x?x:find(fa[x]); }
    
    int kruskal(){
    	int ret=0;
    	for(int i=0;i<maxn;i++) fa[i]=i;
    	sort(egs,egs+m);
    	for(int i=0;i<m;i++){
    		Edge& e=egs[i];
    		int pu=find(e.u);
    		int pv=find(e.v);
    		if(pu!=pv){
    			fa[pv]=pu;
    			ret+=e.w; 
    		}
    	}
    	return ret;
    }
    

    2.2、有向图(最小树形图)

    LL in[maxn];
    int id[maxn], vis[maxn], pre[maxn];
    LL Directed_MST(int rt) {
    	LL ret = 0;
    	while (1) {
    		//求最小入度边
    		for (int i = 0; i < n; i++) in[i] = INF;
    		for (int i = 0; i < m; i++) {
    			Edge& e = egs[i];
    			if (e.w < in[e.v] && e.u != e.v) {
    				in[e.v] = e.w;
    				pre[e.v] = e.u;
    			}
    		}
    		for (int i = 0; i < n; i++) {
    			if (i!=rt&&in[i] == INF) return -1;
    		}
    		int tot = 0;
    		memset(id, -1, sizeof(id));
    		memset(vis, -1, sizeof(vis));
    		in[rt] = 0;
    		//找环,缩点
    		for (int i = 0; i < n; i++) {
    			ret += in[i];
    			int v = i;
    			while (vis[v] != i&&id[v] == -1 && v != rt) {
    				vis[v] = i;
    				v = pre[v];
    			}
    			if (id[v] == -1 && v != rt) {
    				for (int u = pre[v]; u != v; u = pre[u]) {
    					id[u] = tot;
    				}
    				id[v] = tot++;
    			}
    		}
    		//没有环
    		if (tot == 0) break;
    		for (int i = 0; i < n; i++) {
    			if (id[i] == -1) id[i] = tot++;
    		}
    		//更新到环的距离
    		for (int i = 0; i < m; i++) {
    			Edge& e = egs[i];
    			int v = e.v;//这个v要留下来!
    			e.u = id[e.u],e.v = id[e.v];
    			if (e.u != e.v) {
    				e.w -= in[v];
    			}
    		}
    		n = tot;
    		rt = id[rt];
    	}
    	return ret;
    }
    

    2.3、增量最小生成树

    首先找到一颗最小生成树,之后每加一条边,在形成的环上删除权值最大的边。

    2.4、次小生成树

    • 法一:删除最小生成树中的一条边,再重新跑一遍最小生成树
    • 法二:次小生成树一定可以由最小生成树加一条边再删一条边得到,因此只要枚举不在生成树上的没一条边,形成环之后删除环上的最大边就可以了。

    2.5、最小瓶颈路

    • 法一:二分加bfs。
    • 法二:最小生成树上的路就是最小瓶颈路。

    2.6、生成树计数问题

    Matrix-tree:

    //C[i][j]=无相图的度数矩阵-无相图的邻接矩阵
    //Det求n-1阶主子式的行列式
    LL Det(int n) {
    	LL ret = 1;
    	int f = 1;
    	for (int i = 1; i <= n; i++) {
    		for (int j = 1; j <= n; j++) {
    			C[i][j] = (C[i][j] % mod + mod) % mod;
    		}
    	}
    	for (int i = 1; i <= n; i++) {
    		for (int j = i + 1; j <= n; j++) {
    			int A = C[i][i], B = C[j][i];
    			while (B != 0) {
    				LL t = A / B; A = A%B; swap(A, B);
    				for (int k = i; k <= n; k++) {
    					C[i][k] = (C[i][k] - t*C[j][k] % mod + mod) % mod;
    				}
    				for (int k = i; k <= n; k++) {
    					swap(C[i][k], C[j][k]);
    				}
    				f = -f;
    			}
    		}
    		ret = ret*C[i][i] % mod;
    	}
    	if (f == -1) ret = ((-ret) % mod + mod) % mod;
    	return ret;
    }
    

    3、LCA

    3.1、在线倍增

    //dep:深度,fa:父亲,anc:祖先
    int dep[maxn];
    int anc[maxn][maxm];
    void dfs(int u,int f,int d) {
        dep[u]=d;
        anc[u][0]=f;
        for(int i=1; i<maxm; i++) {
            anc[u][i]=anc[anc[u][i-1]][i-1];
        }
    
        for(int p=head[u]; p!=-1; p=egs[p].ne) {
            Edge& e=egs[p];
            if(e.v==f) continue;
            dfs(e.v,u,d+1);
        }
    }
    
    int Lca(int u,int v) {
        if(dep[u]<dep[v]) swap(u,v);
        for(int i=maxm-1; i>=0; i--) {
            if(dep[anc[u][i]]>=dep[v]) {
                u=anc[u][i];
            }
        }
        if(u==v) return u;
        for(int i=maxm-1; i>=0; i--) {
            if(anc[u][i]!=anc[v][i]) {
                u=anc[u][i];
                v=anc[v][i];
            }
        }
        return anc[u][0];
    }
    
    void init(){
        clr(anc[0],0);
    }
  • 相关阅读:
    CentOS6 破解登录密码
    CentOS 添加硬盘创建并挂载分区
    CentOS 安装开发工具包
    CentOS vim的使用
    CentOS tcpdump的使用实例
    CentOS7没有ifconfig/route/arp/netstat等命令的解决方案
    CentOS 查看系统 CPU 个数、核心数、线程数
    Linux系统中的load average
    Python基础-shelve模块
    Python基础-configparser和hashlib模块
  • 原文地址:https://www.cnblogs.com/fenice/p/5702077.html
Copyright © 2020-2023  润新知