zoukankan      html  css  js  c++  java
  • 图论常见解题方法和套路

    一、最短路径问题

    1. 多源最短路径
      Floyd:
    void Floyd(int *d)
    {
          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]);
                        }
          return;
    }
    

    请记住此五行代码。
    一般而言,可以用它判断图两点间的联通情况,称之为传递闭包,此时,仅需将转移的那一行改为:(d[i][j]|=d[i][k]&d[k][j];)

    • 最小环问题(好好看看啊)
    1. 单源最短路径
      以下示均使用邻接表。
      Dijkstra:
    void Dijkstra(int S)
    {
    	priority_queue <pii> Q;
      
    	while(!Q.empty()) Q.pop();
            memset(dis, 0x7f, sizeof(dis);
            memset(vis, false, sizeof(vis));
    	Q.push(make_pair(0, S));
    	dis[S] = 0;
    	while(!Q.empty())
    	{
    		int u = Q.top().second;Q.pop();	
    		if(vis[u]) continue;
    		vis[u] = true;
    		for(int i = 0; i < G[u].size(); ++ i)
    		{
    			int v = G[u][i];
    			if(dis[v] > dis[u] + W[u][i])
    			{
    				dis[v] = dis[u] + W[u][i];
    				Q.push(make_pair(-dis[v], v));
    			}
    		}
    	}
    	return;
    }
    

    spfa:

    void SPFA(int S)
    {
    	queue <int> Q;
    	while(!Q.empty()) Q.pop();
    	memset(inq, false, sizeof(inq));
    	memset(dis, 0x7f, sizeof(dis));
            Q.push(S);
            inq[S] = true;
    	dis[S] = 0;
    
    	while(!Q.empty())
    	{
    		int u = Q.front();
    		Q.pop();
    		inq[u] = false;
    		for(int i = 0; i < G[u].size(); ++ i)
    		{
    			int v = G[u][i], w = W[u][i];
    			if(dis[v] > dis[u] + w)
    			{
    				dis[v] = dis[u] + w;
    				if(!inq[v])
    			        {
    					inq[v] = 1;
    					Q.push(v);
    				}
    			}
    		}
    	}
    	return;
    }
    
    • 若要求得从源点到终点所有路径,能够经过的边权最小的权值时,应将(dis[v] = dis[u] + w)改为(dis[v] = min(w, dis[u])),而求最大值的时候,在Dijkstra还需要把堆写成大根堆;
    • 当判断与源点相连是否存在负环的情况时,应当使用SPFA,在进行松弛操作的基础上加上一句话:(cnt[v] = cnt[u] + 1),其中(cnt[i])指的是源点到节点i最短路径边的条数,则若(cnt[v] ≥ n),那么就判定为负环;
    • 通常情况下,如果求每个节点到源点的最短路时,须要考虑建反图,跑一遍最短路即可;若记录最短路的条数时,应当更新(或松弛)的时候累加;
    • 通常情况下,最短路更可能结合拓扑排序,请留意!

    二、并查集

    既能合并,又能查询的图论神器。
    两种方法中,在图论中首选启发式合并。

    //查询
    int get(int x)
    {
          if(fa[x] == x) return x;
          return get(fa[x]);
    }
    
    //合并
    void merge(int x, int y)
    {
          if(dep[x] < dep[y]) swap(x, y);
          int v = get(y);
          fa[v] = x;
          dep[x] += dep[y];
          return;
    }
    

    主要用于存在多个联通块的情况。
    此结构非常精巧,一定格外留意。

    三、拓扑排序

    不得不说,如果学图论时没有熟练掌握该操作,将是非常的遗憾。
    代码如下:

    void topsort() {
    	while(!Q.empty()) 
          {
                Q.pop();
          }
    	
    	for(int i = 1; i <= n; ++i) {
    		if(deg[i] == 0) {
    			Q.push(i);
    		}
    	}
    	while(!Q.empty()){
    		int x = Q.front();a.push_back(x);
    		Q.pop();
    		for(int i = 0; i < G[x].size(); ++ i){
    			int y = G[x][i];
    			--deg[y];
    			if(deg[y] == 0){
    				Q.push(y);
    			}
    		}
    	}
    	return;
    }
    

    拓扑排序用于判环、找结点的拓扑序等等。

  • 相关阅读:
    CodeForces
    hdu4003 树形dp
    hdu2196
    poj2486
    hdu1502 树形dp入门题
    cf 686D
    bzoj2763 分层图
    hdu4424 并查集+贪心+思维
    poj1734 最小环+输出路径
    集训题解1
  • 原文地址:https://www.cnblogs.com/zach20040914/p/13373077.html
Copyright © 2011-2022 走看看