zoukankan      html  css  js  c++  java
  • 最小生成树

    #include <bits/stdc++.h>
    using namespace std;
    #define MAX 200
    #define INF 0x7fffffff
    //无向图,规定两点间只有一条边
    //下标从1开始
    int graph[MAX+1][MAX+1],points[MAX+1],numbian,numdian,circle[MAX+1];
    struct mydata
    {
        int from,to,cost;
        mydata(int from,int to,int cost){
            this->from=from;
            this->to=to;
            this->cost=cost;
        }
        mydata(){}
        bool operator<(const mydata &a){
            return this->cost<a.cost;
        }
    };
    
    void prim(){
        //加点法
        mydata data[MAX+1];
        int ok[MAX+1];
        memset(ok,0,sizeof(ok));
        for(int i=1;i<=numdian;i++){
            data[i].from=i;
            data[i].to=1;
            data[i].cost=graph[i][1];
        }
        //从点1开始
        ok[1]=1;
        for(int i=0;i<numdian-1;i++){
            int min=-1;
            for(int j=2;j<=numdian;j++){
                //注意这里
                if(!ok[j]&&(min==-1||data[j].cost<data[min].cost)) min=j;
            }
            for(int j=2;j<=numdian;j++){
                //其他所有没加进去的点和这个点的距离
                if(!ok[j]&&graph[min][j]!=-1&&graph[min][j]<data[j].cost){
                    data[j].cost=graph[min][j];
                    data[j].to=min;
                }
            }
            ok[min]=1;
            cout<<data[min].from<<" -> "<<data[min].to<<endl;
        }
        //结果即在data中,有n-1条边
    }
    
    //适合稀疏的图
    void kruskal(){
        //加边法
        int circle[MAX+1],k=1;//区分连通分量
        mydata data[MAX+1];
        for(int i=1;i<=numdian;i++) circle[i]=i;
        for(int i=1;i<=numdian;i++){
            for(int j=i;j<=numdian;j++){
                if(graph[i][j]!=INF){
                    data[k].cost=graph[i][j];
                    data[k].from=i;
                    data[k].to=j;
                    k++;
                }
            }
        }
        sort(data+1,data+numbian+1);
        //注意左闭右开区间形式
        for(int i=1;i<=numbian;i++){
            if(circle[data[i].from]!=circle[data[i].to]){
                cout<<data[i].from<<" -> "<<data[i].to<<endl;
            //更新circle
            int p=circle[data[i].to];
            for(int j=1;j<=numdian;j++){
                //反例!!,这样写不可以,因为circle[data[i].to]的值动态变化,不能比较
                //目的:把所有是to的分量改为from的,但是to本身也会变呀,导致后续比较都错误
                // if(circle[j]==circle[data[i].to])
                //     circle[j]=circle[data[i].from];
                if(circle[j]==p)
                    circle[j]=circle[data[i].from];
            }
            }
        }
    }
    
    
    //kruskal更新一次,查找多次的版本
    //并查集
    // int func(int x){
    //     //这是一个技巧,因为连通分量数组circle初始化为
    //     //1 2 ... n,即为每个点的编号,那么对于circle[i]有
    //     //(1)circle[i]==i,代表他就没变过,所以连通分量编号就是i
    //     //(2)circle[i]!=i,代表被合并了,i合并到了circle[i]里,在返回(1)看看;
    //     //返回值是对应的circle[i]==i的点,修改的也是这个点;
    //     //并且修改所有的circle[x] != x的点;
    //     return circle[x] == x ? x : circle[x] = func(circle[x]);
    // }
    // //改成循环
    // int func1(int x){
    //     if(circle[x] == x) return x;
    //     int k=x;
    //     int *changes=new int[numdian],index=0;
    //     while (circle[k] != k)
    //     {
    //         changes[index++]=k;
    //         k=circle[k];
    //     }
    //     for(int i=0;i<index;i++) circle[changes[i]]=k;
    //     delete[] changes;
    //     return circle[k];
    // }
    
    // bool find(int x,int y){
    //     //找是否在同一个连通分量
    //     //circle按照点的顺序创建
    //     return func(x)==func(y);
    // }
    // void kruskal(){
    //     //加边法
    //     int k=1;//区分连通分量
    //     mydata data[MAX+1];
    //     for(int i=1;i<=numdian;i++) circle[i]=i;
    //     for(int i=1;i<=numdian;i++){
    //         for(int j=i;j<=numdian;j++){
    //             if(graph[i][j]!=INF){
    //                 data[k].cost=graph[i][j];
    //                 data[k].from=i;
    //                 data[k].to=j;
    //                 k++;
    //             }
    //         }
    //     }
    //     sort(data+1,data+numbian+1);
    //     //注意左闭右开区间形式
    //     for(int i=1;i<=numbian;i++){
    //         if(!find(data[i].from,data[i].to)){
    //             cout<<data[i].from<<" -> "<<data[i].to<<endl;
    //             circle[data[i].to]=circle[data[i].from]
    //         }
    //     }
    // }
    
    int main(){
        cin>>numdian>>numbian;
        for(int i=1;i<=numdian;i++)
            for(int j=1;j<=numdian;j++)
                graph[i][j]=INF;
        for(int i=1;i<=numdian;i++) points[i]=i;
        for(int i=0;i<numbian;i++){
            int x,y,z;
            cin>>x>>y>>z;
            graph[x][y]=graph[y][x]=z;
        }
        for(int i=1;i<=numdian;i++){
            for(int j=1;j<=numdian;j++)
                cout<<graph[i][j]<<" ";
            cout<<endl;
        }
    
        // prim();
        kruskal();
        return 0;
    }
  • 相关阅读:
    利用xslt合并多个xml文件到一个文件
    如果利用网络推广老家的特产水果?
    C#并行编程中的Parallel.Invoke
    Asp.Net MVC实现优酷(youku)Web的上传
    修改用户名后TSF出现"需要本地工作区。工作区 xxx 并未驻留在本计算机上"
    JS浏览器滚轮事件实现横向滚动照片展
    Android实现dialog时候弹出软键盘dialog移位问题
    快速搭建多线程Windows服务解决方案
    Difference between WCF and Web API and WCF REST and Web Service
    WPF应用程序的性能提升(一)
  • 原文地址:https://www.cnblogs.com/MorrowWind/p/13056634.html
Copyright © 2011-2022 走看看