圖論相關算法彙總(二)

一. 最小生成樹(Kruscal算法)

#include "iostream"
#include "cstdlib"
#include "cstring"
#include "cstdio"
#define N 10000
#define M 100000
using namespace std;

struct edge //邊集 
{
    int start;  //起點 
    int end;  //終點 
    int weight;  //權值 
}e[M];  
int n,m; //結點數,邊數 
int cmp(const void *a,const void *b);//按升序排列
void kruscal(int *sum);

int main(){
	//freopen("a.txt","r",stdin);
    int i,sum;
  	cin>>n>>m;
    for(i=0;i<m;i++)
    	cin>>e[i].start>>e[i].end>>e[i].weight;//輸入每條邊的權值
    kruscal(&sum);
    cout<<sum<<endl;
	return 0;
}

int cmp(const void *a,const void *b)//按升序排列
{
    return((struct edge*)a)->weight - ((struct edge*)b)->weight ;
}
void kruscal(int *sum){
	int i,k,g,x[N],num;
	for(i=1;i<=n;i++)
        x[i]=i;
    qsort(e,m,sizeof(e[0]),cmp);  //給邊排序 
    *sum=num=0;
    for(i=0;i<m && num < n-1;i++){
        for(k=e[i].start;x[k]!=k;k=x[k])//判斷線段的起始點所在的集合
            x[k]=x[x[k]];
        for(g=e[i].end;x[g]!=g;g=x[g])//判斷線段的終點所在的集合
            x[g]=x[x[g]];
        if(k!=g){     //如果線段的兩個端點所在的集合不一樣
            x[g]=k;
            *sum+=e[i].weight ;
            num++;
            //printf("最小生成樹中加入邊:%d %d\n",e[i].start,e[i].end);
        }
    }

}

二. 最小生成樹(Prim算法)

#include "iostream"
#include "cstdio"
#define M 100000  //最大邊數 
#define N 10000  //最大結點數 
#define INF 100000 
using namespace std;

struct edge{ //邊集 
    int start;  //起點 
    int end;    //終點 
    int weight;  //權值 
}e[M];  
int n,m;   //定點數和邊數 
int getWeight(int v,int w); //求w到v距離
void prim(int fa[],int *sum);

int main(void)
{
	//freopen("a.txt","r",stdin);
	int sum;    //最小生成樹總長 
	int i; 
	cin>>n>>m;
	for(i=0; i<m; i++)
		cin>>e[i].start>>e[i].end>>e[i].weight; 
   	int fa[N];
   	prim(fa,&sum);
   	cout<<sum<<endl;
    return 0;
}

int getWeight(int v,int w){
	int i;
	for(i=0; i<m; i++){
		if((e[i].start==v&&e[i].end==w)||(e[i].start==w&&e[i].end==v))  //無向圖 
			return e[i].weight;
	} 
	return INF;
} 

void prim(int fa[],int *sum){
    int i, j, m, k;
    int d[N];     /*d[j]可以理解成結點j到生成樹(集合)的距離,它的最終值是w[j][fa[j]]*/
    *sum=0;
    for(j=1; j<=n; j++) {
        d[j] = (j == 1 ? 0 :getWeight(1,j));  /*將第一個結點加入集合,並初始化集合與其他結點的距離*/
        fa[j] = 1;   /*當前集合中有且只有一個結點1,其他結點暫時未加入集合,所以沒有父結點,就先姑且初始化成1*/
    }
    for(i=2; i <=n; i++){
        m = INF;
        for(j=1; j<= n; j++)
            if(d[j] <= m && d[j] != 0) 
				m = d[k = j];  /*選取與集合距離最小的邊*/
        *sum=*sum+d[k];   
		d[k] = 0;   /*0在這裏表示與集合沒有距離,也就是說賦值0就是將結點k添加到集合中*/          
        for(j=1; j <=n; j++) /*對剛加入的結點k進行掃描,更新d[j]的值*/
            if(d[j]>getWeight(k,j) && d[j] != 0){
                d[j] = getWeight(k,j); 
                fa[j] = k;
            }
    }
}

三. 單源最短路徑(Dijkstra算法)

#include"iostream"
#include"cstdlib"
#include"cstdio"
#include"cstring"
#define N 100   //頂點最大數 
#define M 10000   //邊最大數 
#define INF 1000000  //兩點之間沒有路徑 
using namespace std;

typedef struct node  //邊節點
{  
   int adjvex;
   int weight;  
   node* next;  
}EdgeNode;  
EdgeNode * node[N+1];  //node[i] 表示頂點i的第一個鄰接點地址,下標從 1 開始 
int D[N+1];       // 保存最短路徑長度 
int p[N+1][N+1];   // 路徑
int final[N+1];  // 若final[i]  =  1則說明 頂點vi已在集合S中
int n,m;      //頂點數和邊數 
void Dijkstra(int v);   // DijKstra
int getWeight(int v,int w);  //得到v到w的距離 

int main()  
{  
	//freopen("a.txt","r",stdin);
	int i; 
	EdgeNode *s;
	cin>>n>>m;  
	for(i=1; i<n; i++)    //初始化 
		node[i] = NULL; 
	for(i=0; i<m; i++){  //輸入邊,鄰接矩陣構建圖 
		int x,y,z;
		cin>>x>>y>>z;
		s = (EdgeNode*)malloc(sizeof(EdgeNode)); 
		s->adjvex = y;       //插入表頭 
		s->weight = z;
		s->next = node[x];
		node[x] = s;
		s = (EdgeNode*)malloc(sizeof(EdgeNode)); //無向圖,若爲有向圖則去掉下面的 
		s->adjvex = x;
		s->weight = z;
		s->next = node[y];
		node[y] = s;
	} 
	int v0=1; 
	Dijkstra(v0);
	for(i=1;i<=n;i++){
		cout<<v0<<"->"<<i<<": "<<D[i]<<endl;
	}

	return 0;
}  

void Dijkstra(int v0){
	int v,w; 
	for (v=1; v<=n; v++){  //循環初始化
		final[v] = 0; 
		D[v] = getWeight(v0,v); 
		for (w=1; w<=n; w++) 
			p[v][w]  =  0;  //設空路徑
		if (D[v]<INF){
			p[v][v0] = 1; 
			p[v][v] = 1;
		}
	}
	D[v0]  =  0; final[v0] = 1; //初始化 v0頂點屬於集合S
	//開始主循環 每次求得v0到某個頂點v的最短路徑 並加v到集合S中
	int i;
	for(i=1; i<=n; i++){
		int min = INF;
		for (w=1; w<=n; w++){    //核心過程--選點
            if (!final[w]){ //如果w頂點在V-S中
            //這個過程最終選出的點 應該是選出當前V-S中與S有關聯邊
            //且權值最小的頂點 ,也可以說是當前離V0最近的點
            	if (D[w]<min) {
					v = w; 
					min = D[w];
				}
        	}
    	}
		final[v] = 1;    //選出該點後加入到合集S中
        for (w=1; w<=n; w++){   //更新當前最短路徑和距離 
            /*在此循環中 v爲當前剛選入集合S中的點
            則以點V爲中間點 考察 d0v+dvw 是否小於 D[w] 如果小於 則更新
            比如加進點 3 則若要考察 D[5] 是否要更新 就 判斷 d(v0-v3) + d(v3-v5) 的和是否小於D[5]
            */
            if (!final[w] && (min+getWeight(v,w)<D[w])){
                D[w] = min+getWeight(v,w);
                // p[w] = p[v];
                p[w][w] = 1; //p[w] = p[v] + [w]
            }
        }
	}
}
int getWeight(int v,int w){
	EdgeNode *p;
	p = node[v];
	while(p!=NULL){
		if(p->adjvex==w)
			return p->weight;
		p=p->next;
	} 
	return INF;
} 



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