求次小生成樹!

鬱悶兩天,先是寫了200多行的kruskal ,後改爲prim算法,只有90行,程序變簡潔了,速度變快了n倍。

這是kruskal算法,由於kruskal算法適用於稀疏圖,故運行時間很慢
雖然我已經用O(n^2)的時間求得了所用兩點之間的唯一的路徑上最大權
值的邊。
方法:
Compute max(u; v) for all vertices in T. Compute for any edge (u; v) not in T the difference
w(u; v) - max(u; v). The two edges yielding the smallest positive difference should be replaced.

代碼:
#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;

#define BUFSIZE 555
#define ULTIMATE 100000000

int set[BUFSIZE];
int height[BUFSIZE];

void merge(int a,int b)
{
    if(height[a]==height[b]){
        height[a]++;
        set[b]=a;
    }else {
        if(height[a]>height[b])
            set[b]=a;
        else set[a]=b;
    }
}

int find(int x)
{
    int r,i,j;

    r=x;
    while(set[r]!=r)r=set[r];
    i=x;
    while(i!=r){
        j=set[i];
        set[i]=r;
        i=j;
    }
    return r;
}

int N;

struct edges{
    int a;
    int b;
    int v;
};

vector<edges> t,r;
int Max[BUFSIZE][BUFSIZE];

struct Edge{
    int v;
    int value;
    Edge *next;
};
struct Node{
    int v;
    bool visited;
    Edge *link;
};

Node G[BUFSIZE];

FILE *fin;
FILE *fout;

void inittree()
{
    for(int i=1;i<=N;i++)
        G[i].link = NULL;
       
    for(int i=0;i<t.size();i++){
        Edge *p = new Edge;
        p->v = t[i].a;
        p->value = t[i].v;
        p->next = G[t[i].b].link;
        G[t[i].b].link = p;
        p = new Edge;
        p->v = t[i].b;
        p->value = t[i].v;
        p->next = G[t[i].a].link;
        G[t[i].a].link = p;
    }
}

int parent[BUFSIZE];

void dfs(int i)
{
    Edge *p = G[i].link;
    G[i].visited = true;
    while(p!=NULL){
        if(!G[p->v].visited){
            parent[p->v] = i;
            dfs(p->v);
        }
        p = p->next;
    }
}

int stack[BUFSIZE];
int top;

void dfs2(int i)
{
    Edge *p = G[i].link;
   
    G[i].visited = true;
    stack[top++] = i;
    while(p!=NULL){
        if(!G[p->v].visited){
            for(int j=0;j<top;j++){
                Max[stack[j]][p->v] = max(p->value,Max[stack[j]][parent[p->v]]);
                Max[p->v][stack[j]] = Max[stack[j]][p->v];
            }
            dfs2(p->v);
        }
        p = p->next;
    }
}

void findmax()
{
    fill_n(&Max[0][0],BUFSIZE*BUFSIZE,0);
   
    dfs(1);
    top = 0;
    for(int i=1;i<=N;i++)
        G[i].visited = false;   
    dfs2(1);
}

int SUM;
int Min;

void secondmin()
{
    inittree();
    findmax();
    Min = ULTIMATE;
    for(int i=0;i<r.size();i++){
        int m = r[i].v - Max[r[i].a][r[i].b];
            if(m>=0)
                Min = min(Min,m);
    }
}

bool comp(edges a,edges b)
{
    return a.v<b.v;
}

edges edge[BUFSIZE];
   
void span()
{
    int m;
    fscanf(fin,"%d%d",&N,&m);
   
    for(int i=1;i<=N;i++)
        set[i] = i;
    fill_n(height,BUFSIZE,0);
   
    for(int i=0;i<m;i++)
        fscanf(fin,"%d%d%d",&edge[i].a,&edge[i].b,&edge[i].v);
       
    sort(edge,edge+m,comp);
    for(int i=0;i<m;i++){
        int p = find(edge[i].a);
        int q = find(edge[i].b);
        if(p!=q){
            t.push_back(edge[i]);
            SUM += edge[i].v;
            merge(p,q);
        }else
            r.push_back(edge[i]);
    }
}

int main()
{
    fin = stdin;
    fout = stdout;

    span();
    secondmin();
    fprintf(fout,"Cost: %d/n",SUM);
    if(Min == ULTIMATE)
        fprintf(fout,"Cost: -1/n");
    else
        fprintf(fout,"Cost: %d/n",SUM+Min);
//    scanf("%d",&N);
    return 0;
}

由於運行慢,這種方法不可取。
想到了寫用heap優化的prim算法用鄰接表作爲存儲結構
有寫了200多行,後來發現用鄰接矩陣會好一些
這是我用鄰接矩陣寫的程序:
#include<iostream>
#include<algorithm>

using namespace std;

#define BUFSIZE 555
#define ULTIMATE 2000000000

struct Node{
    int parent;
    int key;
}node[BUFSIZE];

int N,M,SUM,Min;;
int Max[BUFSIZE][BUFSIZE];
int Graph[BUFSIZE][BUFSIZE];
int stack[BUFSIZE],top;

void init()
{
    fill_n(&Graph[0][0],BUFSIZE*BUFSIZE,ULTIMATE);
   
    scanf("%d%d",&N,&M);
   
    for(int i=1;i<=M;i++){
        int s,d,v;
        scanf("%d%d%d",&s,&d,&v);
        Graph[s][d] = v;
        Graph[d][s] = v;
    }
}
bool comp(Node a,Node b)
{
    if(a.key){
        if(b.key)
            return a.key<=b.key;
        else return true;
    }
    return false;
}
void prim()
{
    SUM = 0;
    for(int j=1;j<N;j++){
        node[j].parent = N;
        node[j].key = Graph[N][j];
    }
    node[N].key = 0;
   
    for(int i=1;i<N;i++){
       
        Node *p = min_element(node+1,node+1+N,comp);
        int k = p - node;
       
        for(int j=0;j<top;j++){
                Max[stack[j]][k] = max(Max[stack[j]][p->parent],p->key);
                Max[k][stack[j]] = Max[stack[j]][k];   //這點很重要,要求v到u(u已經在集合中)的路徑上的最大權值邊
        }                                                                  //只需要取v的父親的Max[u][v'parent] 和value(u,v)的大的那個。
                                                                             // 這樣在prim生成樹的過程中就求得所有點的Max[u][v]。
        SUM += Graph[p->parent][k];
        stack[top++] = k;
        p->key = 0;   //表示點p已經加入了生成樹的集合。
        Graph[k][p->parent] = Graph[p->parent][k] = ULTIMATE;//表示邊k---p->parent不在樹上。
       
        for(int j=1;j<N;j++)
            if(Graph[k][j]<node[j].key){
                node[j].parent = k;
                node[j].key = Graph[k][j];
            }
    }
}
void secondmin()
{
    Min = ULTIMATE;
    for(int i=1;i<=N;i++)
        for(int j=i;j<=N;j++){
            int m = -1;
            if(Graph[i][j]!=ULTIMATE)
                m = Graph[i][j] - Max[i][j];   //只要交換使得m取最小非負值的兩條邊,便得到次小生成樹。
            if(m>=0)
                Min = min(Min,m);
        }
    printf("Cost: %d/n",SUM);
    if(Min == ULTIMATE)
        printf("Cost: -1/n");
    else
        printf("Cost: %d/n",SUM+Min);
}       
int main()
{
    init();
    prim();
    secondmin();
//    scanf("%d",&N);
    return 0;
}

調用c++的min_element只比用binary heap差一點點!!!

上面的kruskal算法同prim的求次小生成樹的原理一樣的,只不過具體實現麻煩一點!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章