作者: 雨落夏至 | 来源:发表于2019-11-02 14:41 被阅读0次

    求解最短路径

    时间复杂度 空间复杂度

    • 单源最短路径
    • 多源最短路径
    • 条数最短(点权为1)
    • 边权之和最小或最大(花费之和)
    • 点权之和最小或最大
    多源最长路径(DAG)(关键路径)
    深度遍历
      void dfs(int cur,int dst){
            if(minpath<dst) return;//当前走过的路径大雨之前的最短路径,没有必要再走下去了
            if(cur==en){//临界条件,当走到终点n
               if(minpath>dst){ minpath=dst; return;  }
            }
            for(int i=1;i<=n;i++){
                if(mark[i]==0&&edge[cur][i]!=inf&&edge[cur][i]!=0){
                    mark[i]=1;
                    dfs(i,dst+edge[cur][i]);
                    mark[i]=0;//需要在深度遍历返回时将访问标志置0
                }
             }
             return;
        }
    
    bellman-ford
    for(k=1;k<=n-1;k++)  //外循环循环n-1次,n为顶点个数
        for(i=1;i<=m;i++)//内循环循环m次,m为边的个数,即枚举每一条边
            if(dis[v[i]]>dis[u[i]]+w[i])//尝试对每一条边进行松弛,与Dijkstra算法相同
                dis[v[i]]=dis[u[i]]+w[i]; 
    

    例子:1003 Emergency (25 分)

    1.以深度优先为基础

    • 在递归函数变量上加上边权/点权这一步改变之后的运算(或者用全局变量,则在递归函数前后写上改变前后的运算,达到一样的效果)
    • 求最大最小边权点权的个数的话只能启用数组
    #include <iostream>
    using namespace std;
    
    const int MAX=500;
    const int INF=10000000;
    int countMax=0,maxTeam[500]={0},c2Profile[500]={INF},k=0,targetProfile=INF,targetTeam=0;
    void DFS(int N,int profile[][MAX],int c1,int c2,int c[],int visited[],int team,int minProfile) {
    
        if(c1==c2){
            maxTeam[k]=team;
            if(targetProfile>minProfile)
                targetProfile=minProfile;
            c2Profile[k]=minProfile;
            k++;
    //        cout<<"-"<<team<<endl;
            return;
        }
    
        visited[c1]=1;
        for (int i = 0; i < N; i++) {
            if (profile[c1][i]!= 0&&visited[i]!=1){
                visited[i]=1;
    //            cout<<"--"<<team<<"+"<<c[i]<<endl;
    //           cout<<i<<"~"<<minProfile<<"~"<<c1<<endl;
                DFS(N, profile, i,c2,c,visited,team + c[i],minProfile + profile[c1][i]);
                visited[i]=0;
            }
        }
    }
    
    int main() {
        int N,M,C1,C2,c1,c2,l;
    
        int c[MAX],visited[MAX]={0};
        int profile[MAX][MAX];
    
        cin >> N >> M >> C1 >> C2;
        for(int i=0;i<N;i++)
            cin >> c[i];
    
        for(int i=0;i<N;i++)
            for(int j=0;j<N;j++)
                profile[i][j]=0;
    
        for(int j=0;j<M;j++){
            cin >> c1 >> c2 >> l;
            profile[c1][c2]=l;
            profile[c2][c1]=l;
        }
    
        DFS(N, profile, C1,C2,c,visited,0,0);
    
        for(int i=0;i<k;i++)
            if(c2Profile[i]==targetProfile) {
                countMax++;
                if(targetTeam<maxTeam[i])
                    targetTeam=maxTeam[i];
            }
        cout << countMax <<" "<< targetTeam+c[C1] <<endl;
        return 0;
    }
    

    2.使用dijkstra

    • 不需要递归!!!
    • 贪心的广度优先思想,把所有点的dist(最短边权)组成集合选出最小值(包括初始点),对这个点周围的所有相关边进行松弛(更新dist)
    • 条数与点权在此基础上完成
    • 无法求负边权

    时间复杂度 O(V²)

    #include <iostream>
    
    using namespace std;
    
    const int MAX = 500;
    const int INF = 10000000;
    int N, M, C1, C2, c1, c2, l;
    int numPath[500] = {0}, maxTeam[500] = {0};
    
    void Dijkstra(int profile[][MAX], int dist[], int visited[], const int team[]) {
        int min = 0, minPath = INF;
        dist[C1] = 0;
        numPath[C1] = 1;
        maxTeam[C1] = team[C1];
        for (int i = 0; i < N; i++) {
            if (profile[C1][i] != 0) {
                dist[i] = profile[C1][i];
            }
    //        cout << dist[i] << endl;
        }
        for (int j = 0; j < N; j++) {
            for (int i = 0; i < N; i++)
                if (visited[i] == 0 && dist[i] < minPath) {
                    minPath = dist[i];
    //                maxTeam[i] = team[i];
                    min = i;
                }
    //        cout << "+" << min << " " << minPath << endl;
            if (min == INF) break;
            visited[min] = 1;
            for (int i = 0; i < N; i++) {
                if (profile[min][i] != 0 && dist[i] > dist[min] + profile[min][i]) {
                    dist[i] = dist[min] + profile[min][i];
                    numPath[i] = numPath[min];
                    maxTeam[i] = maxTeam[min] + team[i];
    //                cout << "-" << i << " " << dist[min] << " " << numPath[i] << " " << maxTeam[i] << endl;
                } else if (profile[min][i] != 0 && dist[i] == dist[min] + profile[min][i]) {
                    numPath[i] = numPath[i] + numPath[min];
                    if (maxTeam[i] < maxTeam[min] + team[i])
                        maxTeam[i] = maxTeam[min] + team[i];
    //                cout << "~" << i << " " << numPath[i] << " " << maxTeam[i] << endl;
                }
            }
            minPath = INF;
        }
    }
    
    int main() {
        int team[MAX] = {0}, dist[MAX], visited[MAX] = {0};
        int profile[MAX][MAX];
    
        cin >> N >> M >> C1 >> C2;
        for (int i = 0; i < N; i++)
            cin >> team[i];
    
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++)
                profile[i][j] = 0;
            dist[i] = INF;
    //        cout<<dist[i]<<endl;
        }
    
        for (int j = 0; j < M; j++) {
            cin >> c1 >> c2 >> l;
            profile[c1][c2] = l;
            profile[c2][c1] = l;
        }
    
        Dijkstra(profile, dist, visited, team);
    
        cout << numPath[C2] << " " << maxTeam[C2] << endl;
        return 0;
    }
    

    3.使用bellman-ford

    • 处理负边权

    时间复杂度 O(VE)

    4.使用SPFA

    • 不推荐

    连通图-拓扑

    无向图

    1.连通:检查连通分支(相连的点的一个小团体)个数

    • 是否连通:连通分支个数=1,则连通;>1,不连通
    • 缺几条边构成连通:缺边数=通分支个数-1
      e.g畅通工程:
    并查集

    2.求桥

    • 什么是桥:在一个无向图中,如果去掉一条边,使得图被分成了几个部分,那么这条边就被称为桥。
    有向图
    离散定义
    • 单向连通一定是弱连通的。but,弱连通不一定是单向连通~~

    1.强连通

    tarjan(u){
      DFN[u]=Low[u]=++Index // 为节点u设定次序编号和Low初值
      Stack.push(u)   // 将节点u压入栈中
      for each (u, v) in E // 枚举每一条边
        if (v is not visted) // 如果节点v未被访问过
            tarjan(v) // 继续向下找
            Low[u] = min(Low[u], Low[v])
        else if (v in S) // 如果节点u还在栈内
            Low[u] = min(Low[u], DFN[v])
      if (DFN[u] == Low[u]) // 如果节点u是强连通分量的根
      repeat v = S.pop  // 将v退栈,为该强连通分量中一个顶点(下三句即打印栈内所有元素)
      print v
      until (u== v)
    }
    
    • kosaraju

      to-do:证明还不是很会,搞不懂为什么一定是 最早时间戳+转置图 的组合
    • poj 2186 Popular Cows

    2.弱连通

    3.半连通

    给定有向图 G = (V, E) ,如果对于所有结点对u,v ∈ V,我们有u→v或v→u,则G是半连通的。请给出一个有效的算法来判断图G是否半连通的。证明算法正确性并分析运行时间。

    • 先强连通缩点,再拓扑求单链
    • 先判断是否是连通,再判断是否是单连通。
    • 引理:有向无环图G(V,E),G是半连通的当且仅当有一条路径,这条路径上有图G中所有点。(可证)
      e.g. [Going from u to v or from v to u?]POJ - 2762

    4.单连通

    对于有向图 G = (V, E) 来说,如果 u ~> v 意味着图 G 包含一条从 u 到 v 的简单路径,则图 G 是单连通图。请给出一个有效算法来判断一个有向图是否单连通图。

    • 八会

    -to be continued

    相关文章

      网友评论

          本文标题:

          本文链接:https://www.haomeiwen.com/subject/vxdeyctx.html