本次課程設計要求在n個城市之間架設n-1條線路,實現這幾個城市之間的網絡通信,要求網絡經濟代價最低。具體要求如下:
根據設計要求,我們假設城市之間的距離越大架設網線的經濟代價越大,因此可以用兩個城市之間的距離作爲邊的權重。
n個城市之間最多可以生成 1+2+...+(n-1)條邊,分別計算出每條邊的長度然後對他們進行升序排序,利用並查集得到由n-1條邊組成的最小生成樹,問題便得到解決。
爲了解決上述問題,需要構建一個城市結構體CITY來表示城市,並且還需要構建EDGE結構體來表示城市與城市的邊,並利用隨機函數生成城市的座標。
本課程設計的全部代碼如下:
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#include<math.h>
#define MaxSize (10000)//n的取值最大爲MaxSize
/*---------------------結構體定義---------------------*/
typedef struct City{
//城市結構體
int id;//城市ID
int x, y;//城市的座標
}CITY;
typedef struct edges{
//邊結構體
int s, e;//s爲起始頂點 e爲終止頂點
double cost;//邊的權值,即兩個頂點之間的距離
}EDGE;
/*---------------------生成城市並顯示---------------------*/
void CreateCityPos(CITY *& city, int n){
//隨機生成城市座標
city = (CITY*)malloc(sizeof(CITY)* n);
srand((unsigned)time(NULL));//設置隨機數的種子
for (int i = 0; i < n; ++i)
{//隨機生成n個城市的x,y座標值
city[i].id = i + 1;
city[i].x = rand() % 100;
city[i].y = rand() % 100;
}
}
void ShowCityPos(CITY*city, int n)
{//顯示城市信息,城市序號、x座標和y座標
printf("\n各城市的編號及座標:\n");
for (int i = 0; i < n; ++i)
{
printf("%d:[%d, %d]\n", city[i].id, city[i].x, city[i].y);
}
}
/*---------------------計算城市兩兩之間的距離,生成邊數組---------------------*/
int Sum(int n)
{//計算n的前n項和,用於根據頂點確定邊的數目 當頂點爲n時 則最多可以產生Sum(n-1)條邊
int sum = 0;
for (int i = 1; i <= n; ++i)
sum += i;
return sum;
}
double CityDist(const CITY*a, const CITY*b)
{//計算兩個城市之間的距離
return sqrt(double((a->x - b->x)*(a->x - b->x) + (a->y - b->y)*(a->y - b->y)));
}
void CreateEdges(EDGE* & e, CITY* city, int n)
{//根據城市信息生成城市之間的邊
e = (EDGE*)malloc(sizeof(EDGE)*Sum(n - 1));//邊的總數爲Sum(n-1)
int cnt = 0;
for (int i = 0; i < n; ++i)
{
for (int k = i + 1; k < n; ++k)
{
(e + cnt)->s = city[i].id;//起始頂點
(e + cnt)->e = city[k].id;//終止頂點
(e + cnt)->cost = CityDist(&city[i], &city[k]);//邊的權值
++cnt;
}
}
}
void ShowCityEdges(EDGE*edges, int n)
{//打印邊信息
printf("\n各城市間的距離(城市1-城市2:邊權值(距離))\n");
//show edges:
for (int i = 0; i < Sum(n-1); ++i)
printf("%d-%d : %f\n", edges[i].s, edges[i].e, edges[i].cost);
}
/*--------------------KrusKal求最小生成樹----------------------*/
int cmp(const void*a, const void *b)
{//比較函數 比較兩條邊的權值 用於排序
EDGE* aa, *bb;
aa = (EDGE*)a; bb = (EDGE*)b;
if ((aa->cost - bb->cost )> 0) return 1;
else return -1;
}
//最小生成樹
int v[MaxSize];
int getRoot(int a)
{//找到根節點
while (a != v[a]) a = v[a];
return a;
}
void KrusKal(EDGE* edges, int n)
{//KrusKal算法生成最小生成樹
int i;
int e, a, b;
double sum = 0.0;
e = Sum(n - 1);
for (i = 0; i < n; ++i) //初始化並查集
v[i] = i;
printf("\n最小生成樹的邊及權值:\n");
for (i = 0; i < e; ++i)
{
a = getRoot(edges[i].s);
b = getRoot(edges[i].e);
if (a != b)
{//將邊併入生成樹
v[a] = b;
printf("%d-%d: %f\n", edges[i].s, edges[i].e, edges[i].cost);//打印併入生成樹的邊的兩個頂點和權值
sum += edges[i].cost;//計算生成樹的總權值
}
}
printf("\n生成樹總權值sum =%f\n", sum);
}
/*------------------------------KrusKal END-------------------------------------*/
void solve(int n)
{
CITY*city;
EDGE* edges;
CreateCityPos(city, n);//創建城市
ShowCityPos(city, n);//顯示城市
CreateEdges(edges, city, n);//創建邊(根據所有城市兩兩之間的距離來創建)
qsort(edges, Sum(n - 1), sizeof(EDGE), cmp);//對邊按權值進行升序排序
ShowCityEdges(edges, n);//顯示排序後的邊
KrusKal(edges, n);//用KrusKal算法生成最小生成樹
}
int main()
{
int n;
printf("請輸入n:");
scanf("%d", &n);
if (n < 2)
return 1;
solve(n);
return 0;
}//運行成功 2019年5月21日10:53:07
/*程序說明:
基本思想:1、首先生成n個城市,每個城市的座標隨機生成,這部分由CreateCityPos()函數實現;
2、計算n個城市兩兩之間的距離(距離計算由CityDist()完成),並保存到邊數組中,這部分由CreateEdges()函數實現;
3、由邊數組(edges[])根據KrusKal算法求最小生成樹,這部分由KrusKal()函數實現,要注意的是進行KrusKal算法之前,需要對edges[]中的元素按照
權值進行升序排序,因此調用了stdlib.h頭文件中的qsort()函數來進行排序。
*/
運行結果:
n爲城市的數量,需要有用戶從終端輸入。