[专题三] 图论

Posted recoverableti

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[专题三] 图论相关的知识,希望对你有一定的参考价值。

图的遍历和应用

  1. 实现方式:邻接矩阵可以使用vector。邻接矩阵的无穷表示方法: memset( road, 0x3f, sizeof(road) );
  2. 应用场景:拓扑图、最小生成树、最短路径、二分图、DFS、BFS。

例题

全排列问题
const int N = 7;
int path[N+1]; bool vset[N+1];
int n;

void dfs(int x) {
    if(x == n) {
        for(int i = 0; i < n; i++) cout << path[i];
        cout << endl;
    }
    else {
        for(int i = 1; i <= n; i++ ) {
            if (vset[i] == false) {
                path[x] = i;
                vset[i] = true;
                dfs(x+1);
                vset[i] = false; //恢复现场
            }
        }
    }
}

int main() {
    scanf("%d", &n);
    memset(vset, false, sizeof vset);
    dfs(0);
}
n皇后
const int N = 10;
char path[N+1][N+1];
bool col[N], zp[2*N], xp[2*N];
int n;

void dfs(int x) {
    if(x == n) {
        for(int i = 0; i < n; i++) puts(path[i]);
        puts("");
    }
    else {
        for(int i = 0; i < n; i++) {
            if (!col[i] && !zp[x + i] && !xp[n - x + i ]) {
                path[x][i] = 'Q';
                col[i] = zp[x + i] = xp[n - x + i ] = true;
                dfs(x+1);
                path[x][i] = '.';
                col[i] = zp[x + i] = xp[n - x + i ] = false;
            }
        }
    }
}

int main() {
    scanf("%d", &n);
    for(int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++){
            path[i][j] = '.';
        }
    }
    dfs(0); //每层遍历
}

迷宫问题

const int N = 100, M = 100;
int path[N][M], vset[N][M];
int n, m;
typedef pair<int, int> PII;
PII q[N * N];

void bfs() {
    int front = 0, rear = 0; //指向的就是队头和队尾 没有多开其他空间
    q[0] = {0, 0};
    int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
    while(front<=rear) {
        auto t = q[front++];
        for(int i = 0; i < 4; i++) {
            int x = t.first + dx[i], y = t.second + dy[i];
            if(x>=0 && x<n && y>=0 && y< m && vset[x][y] == -1 && path[x][y] == 0) {
                vset[x][y] = vset[t.first][t.second] + 1;
                q[++rear] = {x, y};
            }
        }
    }
}

int main() {
    scanf("%d%d", &n, &m);
    for(int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++){
            scanf("%d", &path[i][j]);
            vset[i][j] = -1;
        }
    }
    vset[0][0] = 0;
    bfs();
    printf("%d", vset[n-1][m-1]);
}

有向图的拓扑排序 O(n+e)

 /**
 1. 采用邻接表思想,将vector初始化,每读入一条边,则向v中加入相关结点。
 2. indegree存入度数,结点入队列存储。如果最后入队元素个数等于结点总个数,则返回读入顺序数组,否则返回空数组。
 **/
        int x, y, n;
    queue<int> q;
    vector<int> v;
    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
        vector<int> G[numCourses];
        n = prerequisites.size();
        cout << n << " ";
        int indegree[numCourses];
        for(int i = 0; i < numCourses; i++) {
            indegree[i] = 0;
        }
        for(int i = 0; i < n; i ++) {
            x = prerequisites[i][0];
            y = prerequisites[i][1];
            indegree[x]++;
            G[y].push_back(x);
        }
        for(int i = 0; i < numCourses; i++) {
            cout << indegree[i] << " ";
        }
        for(int i = 0; i < numCourses; i++) {
            if(indegree[i]==0)
            {
                q.push(i);
            }
        }
        while(!q.empty()) { // 判断队列是否为空
            int p = q.front();
            q.pop();
            v.push_back(p);
            for(int j = 0; j < G[p].size(); j++) {
                int k = G[p][j];
                --indegree[k];
                if(indegree[k]==0) q.push(k);
            }
        }
        if(v.size() == numCourses) return v;
        else {
            v.clear();
            return v;
        }
    }

染色法判断二分图

bool check(AGragh *g) {
  int n = g.n;
  // 初始化color数组
  // memset(color, -1, sizeof color);
  int color[maxsize];
  for(int i = 0; i < g.n; i++ ) {
    color[i] = -1;
  }
  int flag = true;
  for(int i = 0; i < n; i++) {
    if(color[i] == -1) { // 遍历数组 找到一个未被染色结点
        if(!dfs(i, 0)) { // 对该结点进行深度优先染色 失败时修改flag
        flag = false; break;
      }
    }
  }
  return flag;
}

bool dfs(int x, int c) { // x是结点 c为结点需要染的颜色
    color[x] = c;
  // 首先将结点x染色 然后遍历其邻接点 染成相反的颜色
  AcrNode *p = g->adjList[x].firstarc;
  while(p){
    int k = p->adjvex;
    if(!color[k]) { //假如其邻接点未被染的 继续深度遍历染色 颜色取反 真是妙啊
      if(!dfs(k,!c)) return false;
      p = p->nextarc;
    } else if(color[k] == c) return false; // 如果该点已经被染色 而且颜色相同的话 返回false
  }
  return ture;
}

匈牙利算法 —— 最大匹配

int match[n]; //match存储的是 右侧点匹配的点
bool st[n]; // 该点是否已经被访问过
int main(AGragh *g) {
  int n = g.n;
  int res = 0;
  memset(st, false, sizeof st);
  memset(match, 0, sizeof match);
  for(int i = 0; i < n; i++) {
    if(find(i)) res++; //对每个点进行匹配
  }
}

bool find(int x) {
    ArcNode *p = g->adjList[x].firstArc; 
    while(p) {
      int j = p->adjvex;
      if(!st[j]){
        st[j] = true; // 如果j结点没有被匹配 或者说 匹配的那个对象可以找到其他人
        if(match[j] == 0 || find(match[j])) {
          match[j] = x;
          return true;
      }
      p = p->nextarc; //    如果已经被匹配就找下一个结点
    }
  }
  return false; //结束了循环还没有被返回 则表明没有找到可以匹配的结点
}

代码与知识点均学习自AcWing:https://www.acwing.com/activity/

以上是关于[专题三] 图论的主要内容,如果未能解决你的问题,请参考以下文章

专题四-图论总结

[考试反思]图论专题测试:怀疑

[专题-图论]最短路

2.16图论专题PB

解题报告图论专题

图论之最小生成树最小生成树专题