天天看點

通訊網絡題目内容

通訊網絡

  • 題目内容
    • 文獻查閱情況
    • 問題解題思路
    • 算法執行情況分析
    • 插傳入連結接與圖檔
    • 運作結果截圖 ![dev c++](https://img-blog.csdnimg.cn/2019011512153220.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQyNDQ1MDYz,size_16,color_FFFFFF,t_70)
    • 總結
    • 參考文獻目錄

題目内容

(通訊網絡)某地區共有n座村莊,每座村莊的坐标(x,y)已知,現在要在村莊之間建立通訊網絡。通訊工具有兩種,分别是需要鋪設的普通線路和無線通訊的衛星裝置。隻能給k個村莊配備衛星裝置,擁有衛星裝置的村莊互相間可直接通訊。鋪設了線路的村莊之間也可以通訊。但是由于技術原因,兩個村莊之間線路長度最多不能超過 d, 否則就會由于信号衰減導緻通訊不可靠。要想增大 d 值,則會導緻要投入更多的裝置(成本)。如何配置設定衛星裝置,才能使各個村莊之間能直接或間接的通訊,并且 d 的值最小?

文獻查閱情況

給定一個無向圖,如果它任意兩個頂點都聯通并且是一棵樹,那麼我們就稱之為生成樹(Spanning Tree)。如果是帶權值的無向圖,那麼權值之和最小的生成樹,我們就稱之為最小生成樹(MST, Minimum Spanning Tree)。

我們由最小生成樹的定義,可以延伸出一個修建道路的問題:把無向圖的每個頂點看作村莊,計劃修建道路使得可以在所有村莊之間通行。把每個村莊之間修建道路的費用看作權值,那麼我們就可以得到一個求解修建道路的最小費用的問題。

常見求解最小生成樹的算法有Kruskal算法和Prim算法。由于篇幅問題再此對于Prim算法,就不多做解釋了。現在我們看看Kruskal算法,是怎麼來求解最小生成樹的問題。

Kruskal算法描述

Kruskal算法是基于貪心的思想得到的。首先我們把所有的邊按照權值先從小到大排列,接着按照順序選取每條邊,如果這條邊的兩個端點不屬于同一集合,那麼就将它們合并,直到所有的點都屬于同一個集合為止。至于怎麼合并到一個集合,那麼這裡我們就可以用到一個工具——-并查集(不知道的同學請移步:Here)。換而言之,Kruskal算法就是基于并查集的貪心算法。

Kruskal算法流程

對于圖G(V,E),以下是算法描述:
           

輸入: 圖G

輸出: 圖G的最小生成樹

具體流程:

(1)将圖G看做一個森林,每個頂點為一棵獨立的樹

(2)将所有的邊加入集合S,即一開始S = E

(3)從S中拿出一條最短的邊(u,v),如果(u,v)不在同一棵樹内,則連接配接u,v合并這兩棵樹,同時将(u,v)加入生成樹的邊集E’

(4)重複(3)直到所有點屬于同一棵樹,邊集E’就是一棵最小生成樹

問題解題思路

第一步模組化,輸入n個村莊坐标Xi,Yi,将村莊看做結點,建立無向完全圖,

接着建立最小生成樹

kruskal算法(避圈法)建立最小生成樹

  • 所有邊按照權值非降次序排序
  • 選取權值最小且沒有回路的邊,記錄選取的邊對應的結點
  • 所有的結點都包含在樹中即可結束,選取(n-1)條邊

共需要邊n-1條。由于隻能k個村莊之間用衛星,即

最小生成樹中最長的k-1條邊需要删去,不鋪設通訊線路,滿足條件為:删

除後,剩餘的邊中,最長的邊要<=d。由于衛星可以連接配接子產品中任意村莊,

是以在建立最小生成樹後,删除邊的過程中,每删除一條邊,需記下被删除

邊的兩個村莊的坐标,即為需要衛星相連接配接的村莊的坐标,輸出衛星配置設定

##算法流程

using namespace std;
#include <math.h>//添加數學函數庫
#define maxn 100
#include<stdio.h>
#include<algorithm>
typedef struct{
    int x, y;
    double w;
}edge;
edge e[maxn];
int rank_[maxn], father[maxn],  sum;
int cmp(edge a, edge b){  //按權值非降序排序(相同則按x坐标)
    if(a.w == b.w)  return a.x < b.x;
    return a.w < b.w;
}

void make_set(int x){
    father[x] = x; 
    rank_[x] = 0;
}

int find_set(int x){
    return  x != father[x] ? find_set(father[x]) : father[x];
}

void union_set(int x, int y, int w){
    sum += w;
    if(x == y)  return ;
    if(rank_[x] > rank_[y])  father[y] = x;
    else{
        if(rank_[x] == rank_[y])  rank_[y]++;
        father[x] = y;
    }
}
//現在隻需要寫檔案輸入n,k,d,加n村莊個坐标,
//輸出得到“每行倆村莊對應 的英文字母“空格” 距離d”
int main(){
	int i,j,n,k,d;
	double D[100][100];//d[1][4]即為第二個村莊與第五個村莊之間的距離 
	FILE*fp1=fopen("in.txt","r");
    //fscanf(fp,"%d %d %d", &n);
	//freopen("in.txt", "r", stdin);//讀檔案 in.txt
	fscanf(fp1,"%d %d %d", &n,&k,&d);//輸出得到“每行倆村莊對應的英文字母  距離d”
    printf("輸入n,k,d   %d %d %d\n", n, k, d);
    for(i = 0; i < n; i++){
        fscanf(fp1,"%d %d", &e[i].x, &e[i].y);
        //getchar(); 
        printf("村莊%c的坐标:( %d , %d )\n", i + 'A', e[i].x, e[i].y);
    }
    fclose(fp1);  //關閉檔案
	printf("\n\n");
	 
    //運算d
    int a,b;
	for(i = 0; i < n; i++)
	for(j = 1; j < n; j++){
	 	a=e[i].x-e[j].x;
	 	b=e[i].y-e[j].y;
	 	if( a < 0)a=-a;
	 	if( b < 0)b=-b;
	 	D[i][j]=sqrt(a*a+b*b);	//添加數學函數庫  printf("%lf\n",sqrt(a)); 3.000000
	} 
	FILE*fp2=fopen("kruskal.txt","w");
	fprintf(fp2,"%d\n",n*(n-1)/2);
	for(i = 0; i < n; i++)
	for(j = i+1; j < n; j++){
	 	fprintf(fp2,"%c %c %lf\n", i + 'A', j + 'A',D[i][j]);	
	}	
	fclose(fp2);//關閉檔案 
int x, y,n1;//kruskal完成最小生成樹 
    char cx, cy;
    FILE*fp=fopen("kruskal.txt","r+");
    fscanf(fp,"%d\n", &n1);
    printf("模組化得n1=%d條邊的無向完全圖\n", n);
    //getchar();
    for(i = 0; i < n1; i++){
        fscanf(fp,"%c %c %lf\n", &cx, &cy, &e[i].w);
        printf("%c %c %f\n", cx, cy, e[i].w);
    }

	freopen("kruskal.txt", "r", stdin);
    scanf("%d", &n1);
    getchar();
    for(i = 0; i < n1; i++){
        scanf("%c %c %lf", &cx, &cy, &e[i].w);
        getchar();
        e[i].x = cx -'A', e[i].y = cy - 'A';
        make_set(i);  //0對應于A
    }
    sort(e, e + n1, cmp);
    printf("n1=%d\n",n1); //問題所在 
    printf("n=%d\n",n);
    a=0;b=0;
    int number=0;//鋪設線路裝置的條數 
    printf("建立最小生成樹:\n");
    	for(i = 0; i < n1; i++){
        x = find_set(e[i].x), y = find_set(e[i].y);
        if(x != y){
            printf("%c-%c : %lf\n", e[i].x + 'A', e[i].y + 'A', e[i].w);
            union_set(x, y, e[i].w);
	}
	printf("依次删除%d條最大邊,形成%d個子產品\n這些子產品用衛星通訊,既保證了d最小,又使得各村莊能通訊\n", k-1, k);
    //printf("total:  %lf\n", sum);
	
	return 0;
}

           

算法執行情況分析

插傳入連結接與圖檔

連結: link.(https://img-blog.csdnimg.cn/20190115115347668.png)

圖檔:

通訊網絡題目内容

//現在隻需要寫檔案輸入n,k,d,加n村莊個坐标,

//輸出得到“每行倆村莊對應的英文字母“空格” 距離d”

用以輸入的檔案及内容,輸入n,k,d,加n村莊個坐标x y

通訊網絡題目内容

用以輸出生成樹的檔案及内容,輸出得到“完全圖的邊數,每行倆村莊對應的英文字母“空格” 距離d”

通訊網絡題目内容

運作結果截圖
通訊網絡題目内容

[dev c++]

總結

由于我是用freopen(“kruskal.txt”, “w”, stdout); //stdout是标準輸出流,預設為螢幕%/向檔案kruskal.txt輸出資料即邊數及每個結點之間的權值,fclose(stdout); //關閉檔案。前一步模組化完成,關閉了儲存模組化資料的檔案kruskal.txt,接着就無法再打開這個檔案進行讀取資料,那麼後面的kruskal算法就無法進行下去。于是我采取了改進方案

(1)在fclose(stdout)之後如果想恢複原來的stdout的話:

fdopen( fd_stdout, “r” );

freopen(“kruskal.txt”, "r ", stdin);//讀檔案kruskal.txt

當然,方案1freopen()——重定向标準輸入輸出流

失敗了,于是采取方案2;

(2)采用指針fp指向檔案,而不是對标準輸入輸出流重定向FILEfp2=fopen(“kruskal.txt”,“w”);

fprintf(fp2,"%d\n",n(n-1)/2);

當然在做這份報告的過程中也遇到了些許難以解決的問題,在翻遍了百度也無法找到我想要的答案的時候,幸好我的同學們伸出了援助之手,真幸運,最後問題得以被解決。

參考文獻目錄

張瑞霞 張敬偉,《資料結構與算法》,—北京:清華大學出版社,2018

張瑞霞主編 《資料結構與算法實驗教程》,—北京:清華大學出版社,2018

古天龍,常亮,《離散數學》,—北京:清華大學出版社,2012.7(2017.8重印)