zoukankan      html  css  js  c++  java
  • 图-邻接矩阵转邻接表+DFS递归非递归+BFS非递归

    #include<iostream>
    #include<vector>
    #include<queue>
    #include<stack>
    using namespace std;
    const int num = 10;
    vector<int> visited(num); //DFS中保存已经被访问过的节点
    
    
    //结点结构类型
    struct ANode {
        int adjvex;     //
        ANode* nextarc; //指向表头结点下一条邻接的边
        int info;       //该边的相关信息,对于带权图可存放权值
    };
    //图的邻接表类型
    struct AGraph {
        vector<ANode*> adjlist; //所有表头组成的数组
        int n, e; //结点个数,边条数
    };
    
    
    //------生成图的邻接表算法------
    void createAdj(AGraph* &G, vector<vector<int> > A, int n) {//n个节点
        //由数组A生成邻接表
        ANode *p;
        G = new AGraph();
        G->n = n;
        G->e = 0;
        for (int i = 0; i < n; i++) {//初始化邻接表头结点
            ANode *anode = new ANode();
            anode->nextarc = NULL;
            G->adjlist.push_back(anode);
        }
        for (int j = 0; j < n; j++) {
            for (int k = n - 1; k >= 0; k--) {//从一行的后往前
                if (A[j][k] != 0) {
                    p = new ANode();
                    p->adjvex = k;
                    p->nextarc = G->adjlist[j]->nextarc;//插入到链表头
                    G->adjlist[j]->nextarc = p;
                    G->e++;
                }
            }
        }
    }
    
    //------输出图的邻接表算法------
    void dispAdj(AGraph *G) {
        ANode* p;
        for (int i = 0; i < G->n; i++) {//头结点,从0开始,n个节点
            cout << i << "->";//头结点
            p = G->adjlist[i]->nextarc;//边节点
            while (p) {
                cout << p->adjvex << "->";//边指向的值
                p = p->nextarc;
            }
            cout << "NULL" << endl;
        }
    }
    
    //深度优先遍历---递归
    void DFS(AGraph *G, int v) {
        //vector<int> visited(G->n);//递归 所以这里错误
        cout << v << " ";
        visited[v] = 1;//标记该值被访问过
        ANode *p = G->adjlist[v]->nextarc;
        while (p) {
            if (!visited[p->adjvex])
                DFS(G, p->adjvex);
            p = p->nextarc;//同层
        }
    }
    //深度优先遍历---非递归
    void DFStranverse(AGraph *G, int v)
    {
        ANode *curr;
        stack<int> s;
        cout << v << " ";
        visited[v] = 1;
        s.push(v);//不需要压入节点指针。因为每次都需要从头结点找下一个而非在pre所在链表找同层节点
        while (!s.empty())
        {    
            curr = G->adjlist[s.top()]->nextarc;//找到头结点G->adjlist[pre->adjvex]继续访问,而非pre
            while (curr)
            {
                //找pre邻接节点中没有访问过的
                if (!visited[curr->adjvex])
                {
                    cout << curr->adjvex << " ";
                    visited[curr->adjvex] = 1;
                    s.push(curr->adjvex);
                    curr = G->adjlist[s.top()]->nextarc;//深度
                }
                else
                {
                    curr = curr->nextarc;
                }
            }
            s.pop();//同层都被访问过
        }
    }
    
    //广度优先遍历---非递归
    void BFS(AGraph* G, int v) {
        ANode *curr;
        queue<int> qu;
        vector<int> visited(G->n);//n个元素的flag用于标明该值是否被访问
        int w;
        cout << v << " ";
        visited[v] = 1;
        qu.push(v);
        while (!qu.empty()) {
            w = qu.front();
            qu.pop();
            curr = G->adjlist[w]->nextarc;//这里不可使用front换掉w,因为front已经不同
            while (curr) {
                if (!visited[curr->adjvex]) {//未被访问
                    cout << curr->adjvex << " ";
                    visited[curr->adjvex] = 1;
                    qu.push(curr->adjvex);//访问后进队,此while访问完同层后,才pop该值,再访问该值邻接的元素
                }
                curr = curr->nextarc;//同层
            }
        }
    }
    
    //测试用例 
    int main() {
        //有向图
        /*int a[] = { 0,1,0,1,0 };
        int b[] = { 0,0,1,1,0 };
        int c[] = { 0,0,0,1,1 };
        int d[] = { 0,0,0,0,0 };
        int e[] = { 1,0,0,1,0 };*/
    
        //无向图
        int a[] = { 0,1,0,1,1 };
        int b[] = { 1,0,1,1,0 };
        int c[] = { 0,1,0,1,1 };
        int d[] = { 1,1,1,0,1 };
        int e[] = { 1,0,1,1,0 };
    
        vector<vector<int> > graph;
        vector<int> A(a, a + 5);//初始化
        vector<int> B(b, b + 5);
        vector<int> C(c, c + 5);
        vector<int> D(d, d + 5);
        vector<int> E(e, e + 5);
    
        graph.push_back(A);
        graph.push_back(B);
        graph.push_back(C);
        graph.push_back(D);
        graph.push_back(E);
    
        AGraph* G;
        int n = 5;//节点数量
        createAdj(G, graph, n);//构造图的邻接表
        cout << "输出图的邻接表:" << endl;
        dispAdj(G);
        cout << endl;
        
        cout << "DFS递归 从0深度优先遍历:" << endl;
        //DFS(G, 0);
        cout << endl;
    
        cout << "DFS非递归 从0深度优先遍历:" << endl;
        DFStranverse(G, 0);
        cout << endl;
    
        cout << "BFS非递归 从0广度优先遍历" << endl;
        BFS(G, 0);
        return 0;
    }

    图-最短路径-dijkstra、bellmanford、spfa、floyd

    /*
    最短路径 
    https://blog.csdn.net/YF_Li123/article/details/74090301 dijkstra算法
    https://blog.csdn.net/YF_Li123/article/details/74090409 dijkstra笔试题只需要修改更新d[k]处
    http://www.voidcn.com/article/p-pwheyixu-te.html 优化
    */
    #include<iostream>
    #include<vector>
    #include<algorithm>
    #include<queue>
    using namespace std;
    
    /*一、dijkstra迪杰斯特拉算法
    对比 最小生成树的prim算法
    */
    
    const int INF = 1000000000;
    /*
    1、邻接矩阵 求s到图G中其他顶点的最短路径
    d是起点s到图中每个顶点的 最短距离
    pre是s到每一节点的前驱节点,用于后续输出s到某个节点的 最短路径
    */
    void Dijkstra(int n, int s, vector<vector<int> > G, vector<bool>& vis, vector<int>& d, vector<int> &pre)
    {
        for (int i = 0; i < n; i++)//pre用于后续输出s到其他顶点的最短路径
            pre[i] = i;
    
        //vector<bool> vis(n, false);
        //vector<int> d(n);
        fill(d.begin(), d.end(), INF);//s与其余节点距离无限大
        d[s] = 0;
        for (int i = 0; i < n; i++)//操作节点个数n次
        {
            int u = -1;//与当前节点最小距离的未访问节点
            int mindistance = INF;
            for (int j = 0; j < n; j++)
            {
                if (vis[j]==false && d[j] < mindistance)
                {
                    mindistance = d[j];
                    u = j;
                }
            }
            if (u == -1)//不连通
                return ;
            vis[u] = true;
            //以中介点u更新d。d为s到图中每个顶点的最短距离
            for (int k = 0; k < n; k++)//遍历u可到达的未访问节点 以u为中介可以让s到k的距离d[k]更优 则更新d[k]【对比最小生成树的prim算法
            {
                if (!vis[k] && G[u][k] + d[u] < d[k])
                {
                    d[k] = G[u][k] + d[u];
                    pre[k] = u;//pre记录u->k 用于后续输出最短路径
                }
            }
        }
    }
    
    /*
    2、邻接表
    */
    struct Node
    {
        int v;//连接的节点值
        int dis;//权值
        Node(int x, int y) :v(x), dis(y) {}
    };
    void Dijkstra1(int n, int s, vector<vector<Node> > G, vector<bool>& vis, vector<int>& d, vector<int> &pre)
    {
        for (int i = 0; i < n; i++)//pre用于后续输出s到其他顶点的最短路径
            pre[i] = i;
    
        //vector<bool> vis(n, false);
        //vector<int> d(n);
        fill(d.begin(), d.end(), INF);//s与其余节点距离无限大
        d[s] = 0;
        for (int i = 0; i < n; i++)//操作节点个数n次
        {
            int u = -1;//与当前节点最小距离的未访问节点
            int mindistance = INF;
            for (int j = 0; j < n; j++)
            {
                if (vis[j] == false && d[j] < mindistance)
                {
                    mindistance = d[j];
                    u = j;
                }
            }
            if (u == -1)//不连通
                return ;
            vis[u] = true;
            //上面和Dijkstra相同
            for (int k = 0; k < G[u].size(); k++)//更新u与其余可连接的未访问节点(共G[u].size()个)的距离。实际是用原距离更新INF
            {
                int tmp = G[u][k].v;//未访问节点的值
                if (!vis[tmp] && G[u][k].dis + d[u] < d[tmp])
                {
                    d[tmp] = G[u][k].dis + d[u];
                    pre[tmp] = u;
                }
            }
        }
    }
    
    /*
    优化最短路径迪杰斯特拉:堆排序的priority_queue存储最短距离、目标节点编号,即不用遍历所有节点找最短路径(第二个for
    原On^2 优化OElogn
    (更新最短距离只需访问每条可到达的边OE,【查找下一个可使用最短目标节点Ologn(原方法需要枚举所有节点比较)】)
    
    */
    void Dijkstra_good(int n, int s, vector<vector<int> > G, vector<bool>& vis, vector<int>& d, vector<int> &pre)
    {
        fill(d.begin(), d.end(), INF);
        d[s] = 0;
        for (int i = 0; i < n; i++)
            pre[i] = i;
        priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > pq;//最小堆
        pq.push(make_pair(0, s));
        while (!pq.empty())
        {
            pair<int, int> toppair = pq.top();
            pq.pop();
            int mindistance = toppair.first;//当前节点能到达的最短距离
            int tonode = toppair.second;//当前节点能到达的最短距离的目标节点编号
            if (vis[tonode])
                continue;//当前最小距离节点已经访问过
            vis[tonode] = true;
            
            for (int i = 0; i < G[tonode].size(); i++)//以目标节点为中介 更新s到其余节点的最短距离
            {
                if (!vis[i] && mindistance + G[tonode][i] < d[i])
                {
                    d[i] = mindistance + G[tonode][i];
                    pre[i] = tonode;
                    pq.push(make_pair(d[i], i));//把s经过中介tonode到可到达节点i的最短距离、目标节点编号加入优先级队列(自己会排序
                }
            }
        }
    }
    
    /*
    bellmanford有负权  外层1到n-1次循环,每次松弛所有边,最后再循环所有边判断是否有负边权
    */
    struct Edge
    {
        int u, v;
        int weight;
        Edge(int x, int y, int z) :u(x), v(y), weight(z) {}
    };
    /*返回是否有最短路径,即有负边权则false*/
    bool bellmanford(int n, int m, int s, vector<Edge> &E, vector<int> &d, vector<int> &pre)
    {
        fill(d.begin(), d.end(), INF);
        d[s] = 0;
        for (int i = 0; i < n; i++)
            pre[i] = i;
        for (int i = 1; i < n; i++)
        {
            for (int j = 0; j < m; j++)
            {
                if (d[E[j].v] > d[E[j].u] + E[j].weight)//s->v的距离 > s->u->v的距离,则更新
                {
                    d[E[j].v] = d[E[j].u] + E[j].weight;
                    pre[E[j].v] = E[j].u;
                }
            }
        }
        for (int j = 0; j < m; j++)//最后判定是否有负边权。没收敛
        {
            if (d[E[j].v] > d[E[j].u] + E[j].weight)
            {
                cout << "xxxxxxxxxxxxxx" << endl;
                return false;
            }
        }
        return true;
    }
    
    /*
    3、SPFA 队列优化bellmanford 单源 负边权
    */
    bool SPFA(int n, int s, vector<vector<int> > G, vector<int> &d, vector<bool> &vis, vector<int> &pre)
    {
        fill(d.begin(), d.end(), INF);
        d[s] = 0;
        for (int i = 0; i < n; i++)
            pre[i] = i;
        vector<int> flag(n, 0);//记录每个节点入队次数,如果n次,说明有负环路
        queue<int> q;//队列优化
        q.push(s);
        while (!q.empty())
        {
            int curr = q.front();
            q.pop();//从队列删除
            vis[curr] = false;//curr不在当前队列
            for (int i = 0; i < n; i++)//循环n-1次
            {
                if (G[curr][i] != INF && d[i] > d[curr] + G[curr][i])
                {
                    d[i] = d[curr] + G[curr][i];
                    if (!vis[i])
                    {                    
                        vis[i] = true;
                        flag[i]++;
                        if (flag[i] >= n)//进队n次 有负环路
                            return false;
                        q.push(i);
                    }
                    pre[i] = curr;
                }
            }
        }
        return true;
    }
    
    
    /*输出s到v的最短路径*/
    void Printresult(int s, int v,vector<int> pre)
    {
        if (s == v)
        {
            cout << s << " ";
            return;
        }
        Printresult(s, pre[v], pre);//s到v的前一节点路径
        cout << v << " ";
    }
    
    /*floyd多源最短路径*/
    void floyd(int n, vector<vector<int> > &e, vector<vector<int> > &path)
    {
        for (int k = 0; k < n; k++)
            for (int i = 0; i < n; i++)
                for (int j = 0; j < n; j++)
                    if (e[i][j] > e[i][k] + e[k][j])
                    {
                        e[i][j] = e[i][k] + e[k][j];
                        path[i][j] = path[i][k];
                    }
    }
    
    int main()
    {
        //1、dijkstra算法
        int n = 6;
        vector<vector<int>> G = { {0,1,INF,4,4,INF},
                                  {INF,0,INF,2,INF,INF},
                                  {INF,INF,0,INF,INF,1},
                                  {INF,INF,2,0,3,INF},
                                  {INF,INF,INF,INF,0,3},
                                  {INF,INF,INF,INF,5,0} };//有向图
        
        vector<vector<Node>> Adj = {
        {Node(1,1),Node(3,4),Node(4,4)},
        {Node(3,2)},
        {Node(5,1)},
        {Node(2,2),Node(3,4)},
        {Node(3,5)},
        {Node(4,5)}};
    
        vector<bool> vis(n,false);
        vector<int> d(n);//保存起始节点s到图中每个节点的距离
        vector<int> pre(n);
        //Dijkstra(n, 0, G, vis, d, pre);//邻接矩阵
        //Dijkstra1(n, 0, Adj, vis, d, pre);//邻接表
        //Dijkstra_good(n, 0, G, vis, d, pre);//priority_queue堆排序优化原On^2到OElogn  E是边n是节点
        
        //2、bellmanford 可判断负边权
        vector<Edge> E = { Edge(0,1,1),Edge(0,3,4),Edge(0,4,4),Edge(1,3,2),Edge(2,5,1),
                     Edge(3,2,2),Edge(3,4,3),Edge(4,5,3),Edge(5,4,5)};//没有负环路
        int b_n = 6;
        int b_m = 9;
        //bool nonegative = bellmanford(b_n, b_m, 0, E, d, pre);
        //vector<Edge> test = { Edge(0,1,5),Edge(1,2,3),Edge(2,0,-10)};//有负环路
        //bool nonegative = bellmanford(3, 3, 0, test, d, pre);    
        
        //3、SPFA 队列优化bellmanford
        vector<vector<int>> G1 = { {0,5,INF},
                                  {INF,0,3},
                                  {-10,0,INF}};
        //bool nonegative=SPFA(3, 0, G1, d, vis, pre);//负环路
        //bool nonegative = SPFA(n, 0, G, d, vis, pre);//无负环路
        //cout << "有最短路径,无负环路:" << nonegative << endl;
        
        /*cout << "起始点0到其余节点的最短距离:" << endl;
        for (auto x : d)
            cout << x << " ";
        cout << endl;
    
        //输出从起点s到顶点v的最短路径
        cout << "从起点s到顶点v的最短路径:" << endl;*/
        //Printresult(0, 5, pre);
        
        //4、floyd多源 动态规划
        vector<vector<int> > path(G.size(),vector<int>(G[0].size()));//初始化后用于输出具体路径的二维数组path
        for (int i = 0; i < G.size(); i++)
            for (int j = 0; j < G[0].size(); j++)
                path[i][j] = j;
    
        floyd(n, G, path);//会修改G存储多源最短距离
        for (int i = 0; i < n; ++i)//每个顶点到其他顶点最短距离
        {
            for (int j = 0; j < n; ++j)
            {
                if (G[i][j] != INF)//如果存在路径
                {
                    //判断负环是否存在只需检查是否存在d[i][i]是负数的顶点i
                    cout << "dis[" << i << "][" << j << "] = " << G[i][j];//最短距离
    
                    //最短路径
                    cout << "  路径:";
                    cout << i << " ";
                    int temp = path[i][j];
                    while (temp != j)
                    {
                        cout << temp << " "; //经过节点
                        temp = path[temp][j];
                    }
                    cout << j << " " << endl;
                }
                else
                    cout << i << " cant to " << j << endl;
            }
        }
        
        return 0;
    }

    图-最小生成树prim+kruskal

    /*
    https://blog.csdn.net/YF_Li123/article/details/75148998
    https://blog.csdn.net/YF_Li123/article/details/75195549
    */
    #include<iostream>
    #include<vector>
    #include<algorithm>
    using namespace std;
    
    /*一、prim普里姆算法*/
    const int INF = 1000000000;//除了d[s]=0,s和其余节点初始距离无限大
    
    /*
    1、邻接矩阵实现最小生成树
    n节点个数,s初始节点,G图的邻接矩阵,vis已访问节点,d其余未访问节点与集合S的最短距离(其余未访问节点与当前节点u的最短距离
    返回最小权重和
    */
    int Prim(int n, int s, vector<vector<int> > G)
    {
        vector<bool> vis(n, false);
        vector<int> d(n);
        fill(d.begin(), d.end(), INF);//s与其余节点距离无限大
        d[s] = 0;
        int result = 0;//最小权重和 返回
        for (int i = 0; i < n; i++)//操作节点个数n次
        {
            int u = -1;//与当前节点最小距离的未访问节点
            int mindistance = INF;
            for (int j = 0; j < n; j++)
            {
                if (vis[j]==false && d[j] < mindistance)
                {
                    mindistance = d[j];
                    u = j;
                }
            }
            if (u == -1)//不连通
                return -1;
            vis[u] = true;
            result += d[u];
            for (int k = 0; k < n; k++)//更新u与其余可连接的未访问节点的距离。实际是用原距离更新INF
            {
                if (!vis[k] && G[u][k] != INF && G[u][k] < d[k])
                    d[k] = G[u][k];
            }
        }
        return result;
    }
    
    /*
    2、邻接表实现最小生成树
    n节点个数,s初始节点,G图的邻接表,vis已访问节点,d其余未访问节点与集合S的最短距离(其余未访问节点与当前节点u的最短距离
    返回最小权重和
    */
    struct Node
    {
        int v;//连接的节点值
        int dis;//权值
        Node(int x, int y) :v(x), dis(y) {}
    };
    int Prim1(int n, int s, vector<vector<Node> > G)
    {
        vector<bool> vis(n, false);
        vector<int> d(n);
        fill(d.begin(), d.end(), INF);//s与其余节点距离无限大
        d[s] = 0;
        int result = 0;//最小权重和 返回
        for (int i = 0; i < n; i++)//操作节点个数n次
        {
            int u = -1;//与当前节点最小距离的未访问节点
            int mindistance = INF;
            for (int j = 0; j < n; j++)
            {
                if (vis[j] == false && d[j] < mindistance)
                {
                    mindistance = d[j];
                    u = j;
                }
            }
            if (u == -1)//不连通
                return -1;
            vis[u] = true;
            result += d[u];
            //上面和prim相同
            for (int k = 0; k < G[u].size(); k++)//更新u与其余可连接的未访问节点(共G[u].size()个)的距离。实际是用原距离更新INF
            {
                int tmp = G[u][k].v;//未访问节点的值
                if (!vis[tmp] && G[u][k].dis != INF && G[u][k].dis < d[tmp])
                    d[tmp] = G[u][k].dis;
            }
        }
        return result;
    }
    
    /*二、kruskal克鲁斯卡尔算法*/
    struct Edge
    {
        int u, v;
        int weight;
        Edge(int x, int y, int z) :u(x), v(y), weight(z) {}
    };
    bool cmp(Edge a, Edge b)
    {
        return a.weight < b.weight;
    }
    /*并查集查找。寻找s的所在集合的根节点*/
    int findfather(vector<int> &father, int s)
    {
        while (father[s] != s)
            s = father[s];
        return s;
    }
    /*
    并查集https://blog.csdn.net/CoderPai/article/details/69388597 
    并查集查找优化:对于每个节点,一旦向上走到了一次根节点,就把这个点到父亲的边改为直接连向根*/
    int findfather_good(vector<int> &father, int s)
    {
        int tmp = s;
        while (s != father[s])//得到s的根节点 给s
            s = father[s];
        while (tmp != father[tmp])//优化s的所有父节点直接指向根,下次找父节点就路径压缩
        {
            int ttmp = tmp;//先记录当前节点值
            tmp = father[tmp];//让tmp指向父节点,用于下一轮更新父节点直接指向根
            father[ttmp] = s;//直接让当前节点的父节点指向根
        }
    }
    
    /*
    n为图中节点数,m为边数,E为边集合
    */
    int kruskal(int n,int m,vector<Edge> &E)
    {
        int result = 0;//最小生成树 权重结果
        int numofedge = 0;//已加入最小生成树的边数量
        vector<int> father(n);//并查集。用于判断边的两个节点是否已连接
        for (int i = 0; i < n; i++)//并查集初始化
            father[i] = i;
        sort(E.begin(), E.end(), cmp);//升序排列所有边权重
        for (int i = 0; i < m; i++)//循环边次数
        {
            /*当前边E[i]的两个顶点是否连通*/
            int fau = findfather(father, E[i].u);//并查集查找 u所在集合的根节点
            int fav = findfather(father, E[i].v);
            if (fau != fav)//不连通,合并表示加入最小生成树,连通
            {
                father[fau] = fav;//并查集合并
                result += E[i].weight;
                numofedge++;
                if (numofedge == n - 1)//如果边numofedge=节点n-1则有最小生成树,提前退出
                    break;
            }
        }
        if (numofedge != n - 1)//不连通
            return -1;
        else
            return result;
    }
    
    int main()
    {
        //最小生成树 2种算法实现
        /*1、prim算法*/
        int n = 6;
        /*邻接矩阵*/
        vector<vector<int>> G = { 
        {0,4,INF,INF,1,2},
        {4,0,6,INF,INF,3},
        {INF,6,0,6,INF,5},
        {INF,INF,6,0,4,5},
        {1,INF,INF,4,0,3},
        {2,3,5,5,3,0} };
    
        /*邻接表*/
        vector<vector<Node>> Adj = { 
        {Node(4,1),Node(5,2),Node(1,4)},
        {Node(0,4),Node(5,3),Node(2,6)},
        {Node(1,6),Node(3,6),Node(5,5)},
        {Node(2,6),Node(4,4),Node(5,5)},
        {Node(0,1),Node(5,3),Node(3,4)},
        {Node(0,2),Node(1,3),Node(2,5),Node(3,5),Node(4,3)} };
    
        /*for (auto x : Adj)
        {
            for (auto y : x)
                cout << y.v<<"-"<<y.dis << "  ";
            cout << endl;
        }*/
    
        int res1 = Prim(n, 0, G);//邻接矩阵版
        cout << res1 << endl;
        int res2 = Prim1(n, 0, Adj);//邻接表版
        cout << res2 << endl;
    
        //2、kruskal克鲁斯卡尔算法*/
        vector<Edge> E = { Edge(0,1,4),Edge(1,2,1),Edge(2,3,6),Edge(3,4,5),Edge(0,4,1),
                              Edge(0,5,2),Edge(1,5,3),Edge(2,5,5),Edge(3,5,4),Edge(4,5,3) };
        int k_n = 6;//节点数
        int k_m = 10;//边数
        int result = kruskal(k_n, k_m, E);
        cout << result << endl;
    
        return 0;
    }
  • 相关阅读:
    Android引导页设计
    QQ5.0左侧滑动显示效果
    QQ左侧滑动显示之按钮切换
    QQ左侧滑动显示之自定义属性
    HDU 4009——Transfer water——————【最小树形图、不定根】
    HDU 2121——Ice_cream’s world II——————【最小树形图、不定根】
    POJ 3164——Command Network——————【最小树形图、固定根】
    HDU 5532——Almost Sorted Array——————【技巧】
    UVA 10462 —— Is There A Second Way Left?——————【最小生成树、kruskal、重边】
    HDU 4081—— Qin Shi Huang's National Road System——————【次小生成树、prim】
  • 原文地址:https://www.cnblogs.com/beixiaobei/p/10914262.html
Copyright © 2011-2022 走看看