问题描述
好的,那么。我有一个程序,我已经研究了一段时间了。该项目包括寻找连接所有城市的最便宜的方式——通过机场或公路连接。我为此使用了 Kruskal 的算法,并解释说城市是图形顶点,道路是城市之间的边,机场通过“天空”顶点连接到其他城市。然而,这个项目的一个要求是,当有一个城市既没有公路也没有机场连接时,它应该给用户以下输出:“不足”。
完整代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Edge {
int src,dest,weight;
};
struct Graph {
int V;
int E;
struct Edge* edge;
};
struct Graph* createGraph(int V,int E)
{
struct Graph* graph = malloc(sizeof *graph);
graph->V = V;
graph->E = E;
graph->edge = (struct Edge*)malloc(E * sizeof(struct Edge));
return graph;
};
struct subset {
int parent;
int rank;
};
int find(struct subset subsets[],int i)
{
if (subsets[i].parent != i)
subsets[i].parent
= find(subsets,subsets[i].parent);
return subsets[i].parent;
}
void Union(struct subset subsets[],int x,int y)
{
int xroot = find(subsets,x);
int yroot = find(subsets,y);
if (subsets[xroot].rank < subsets[yroot].rank)
subsets[xroot].parent = yroot;
else if (subsets[xroot].rank > subsets[yroot].rank)
subsets[yroot].parent = xroot;
else
{
subsets[yroot].parent = xroot;
subsets[xroot].rank++;
}
}
int myComp(const void* a,const void* b)
{
struct Edge* a1 = (struct Edge*)a;
struct Edge* b1 = (struct Edge*)b;
return a1->weight > b1->weight;
}
void KruskalMST(struct Graph* graph)
{
int V = graph->V;
struct Edge
result[V];
int e = 0;
int i = 0;
qsort(graph->edge,graph->E,sizeof(graph->edge[0]),myComp);
struct subset* subsets
= (struct subset*)malloc(V * sizeof(struct subset));
for (int v = 0; v < V; ++v) {
subsets[v].parent = v;
subsets[v].rank = 0;
}
while (e < V - 1 && i < graph->E) {
struct Edge next_edge = graph->edge[i++];
int x = find(subsets,next_edge.src);
int y = find(subsets,next_edge.dest);
if (x != y) {
result[e++] = next_edge;
Union(subsets,x,y);
}
}
printf("MST edges:\n");
printf("V1 V2 Cost\n");
int minimumCost = 0;
int nRoads = 0;
int nAirports = 0;
for (i = 0; i < e; ++i)
{
printf("%d -- %d == %d\n",result[i].src,result[i].dest,result[i].weight);
if (result[i].dest == 0) {
nAirports++;
}
else {
nRoads++;
}
minimumCost += result[i].weight;
}
printf("Minimum Spanning Tree total cost: %d\n",minimumCost);
printf("Number de airports: %d\n",nAirports);
printf("Number of roads: %d",nTotal);
}
return;
}
int main()
{
int v = 0;
int a = 0;
int edges = 0;
int r = 0;
int city = 0;
int airport = 0;
int city1 = 0;
int city2 = 0;
int cost = 0;
printf("Insert the number of cities: \n");
scanf("%d",&v);
printf("Insert the number of airports: \n");
scanf("%d",&a);
printf("Insert the number of roads: \n");
scanf("%d",&r);
edges = a + r;
struct Graph* graph = createGraph(v,edges);
for (int i = 0; i < a; i++) {
printf("Insert the city and the cost of building the airport: \n");
scanf(" %d %d",&city,&airport);
graph->edge[i].src = city;
graph->edge[i].dest = 0;
graph->edge[i].weight = airport;
}
for (int j = a; j < edges; j++) {
printf("Insert the cities and the cost of the road: \n");
scanf(" %d %d %d",&city1,&city2,&cost);
graph->edge[j].src = city1;
graph->edge[j].dest = city2;
graph->edge[j].weight = cost;
}
KruskalMST(graph);
return 0;
}
到目前为止,我试图寻找为什么当所有顶点都没有连接并且我找不到位置时它没有返回错误。如果出现错误,我可以使用 setjmp/longjmp 来模拟捕获错误,但是,为此,我需要先出现错误。
解决方法
根据我从您的代码中得出的结论,您没有任何方法可以输入与另一个顶点没有至少一个连接的顶点。
无向图由两组不同的信息组成:
- 顶点
- 边缘
我认为您有一个边数组,其中数组索引本身就是顶点标签,这很好,只要您只需要表示连接的顶点即可。
Kruskal 算法在 C 中最常见和最紧凑的实现,使用二维数组作为 [vertices][edges]。您的代码由一个包含一系列边的“图形”组成。
在线快速搜索,找到了一些示例,但它们似乎都适用于完全连接的网络。您的任务似乎包括网络未完全连接的要求,因此;你需要一种方法来输入和存储一个没有连接到任何东西的顶点。换句话说,您需要能够向图中添加不与其他任何连接的节点。
根据您的代码,您可能能够创建未连接的岛,但您必须注意那些与 V0 没有直接或间接连接的岛。您还必须添加代码来检测这些岛屿。
您首先需要的是一个具有两个或多个孤岛网络的数据集。您的代码将允许您创建的最小岛是由一条道路连接的一对顶点。如果您先创建它,然后确保您定义的剩余边都没有以任何方式连接到第一对边,那么您将至少有两个岛。
然后你运行你的程序,看看会发生什么。它可能会失败,但现在您有了一个数据集,可以用来测试对代码的更改。我们称之为单元测试。您需要两个简单的数据集。一组完全连接,一组有岛屿。您可以将程序输入存储在文件中并将它们通过管道传输到您的应用中:
inputPartiallyConnected.txt | YourApp
inputFullyConnected.txt | YourApp
将其输入到您的程序中。左边的值可以存储在一个文件中。弯曲的箭头是回车。您的代码需要识别 1 和 2 无法从任何其他节点访问。
正文如下:
6
1
8
3 8
1 2 10
3 4 11
5 6 6
4 6 7
5 4 5
5 4 1
6 3 2
您可以通过在 2 和 4 之间或 1 和 3 之间添加一条道路来创建一个完全连接的版本。保持简单。
您的 Kruskal 算法实现将无法在多个森林中正常工作。上面的数据集本质上是两个森林。您可以通过要求每条新边(除了第一条边)至少包含一个在先前输入的边之一中引用的顶点来防止用户输入多个森林。
所以
- 对于第一条边之后的每条新边,
- 对于新边中的每个顶点,
- 搜索该顶点的所有前边。
- 如果在前面的边中都没有找到,
- 然后你可以发出“Insufficient”输出并丢弃坏边,
- 然后再次提示用户输入。
- 冲洗并重复。
因为,在上面的测试数据集中,我们输入 1--2 后跟 3--4,我们应该得到一个 Insufficient
警告,对于后面的每一条边,因为它们不包含 1 或2 在他们的顶点集中。因此,一旦您的代码成功拒绝了上述列表中除 1--2 之外的所有内容,您就可以对其进行重新排序,以便您始终构建一个完全连接的图(又名:单棵树)。
如果您的任务是接受一片树林,那么您必须首先找到岛屿,然后通过 Kruskal 算法分别运行每个岛屿。