问题 F: 还是畅通工程
时间限制: 1 Sec 内存限制: 32 MB提交: 18 解决: 10 [][][]题目描述
某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。
输入
测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( < 100 );随后的N(N-1)/2行对应村庄间的距离,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间的距离。为简单起见,村庄从1到N编号。 当N为0时,输入结束,该用例不被处理。
输出
对每个测试用例,在1行里输出最小的公路总长度。
样例输入
81 2 421 3 681 4 351 5 11 6 701 7 251 8 792 3 592 4 632 5 652 6 62 7 462 8 823 4 283 5 623 6 923 7 963 8 434 5 284 6 374 7 924 8 55 6 35 7 545 8 936 7 836 8 227 8 170
样例输出
82
#include#include using namespace std;#define N 5100struct edge{ int u,v,w;};edge e[N];int f[N];int cmp(edge a,edge b){ return a.w < b.w ;}int find(int a){ if(f[a] == a) return a; else { f[a] = find(f[a]); return f[a]; }}int merge(int a,int b){ int t1,t2; t1 = find(a); t2 = find(b); if(t1 !=t2) { f[t1] = t2; return 1; } return 0;}int main(){ int n,i,j,m,count,sum; while(scanf("%d",&n),n!=0) { m = (n*(n-1))/2; for(i = 1; i <= m; i ++) scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w); sort(e+1,e+1+m,cmp); count = sum = 0; for(i = 1; i <= n; i ++) f[i] = i; for(i = 1; i <= m; i ++) { if(merge(e[i].u,e[i].v)) { sum += e[i].w ; count ++; } if(count == n-1) break; } printf("%d\n",sum); } return 0;}
问题 C: 算法7-9:最小生成树
时间限制: 1 Sec 内存限制: 32 MB 提交: 16 解决: 10 [ ][ ][ ]题目描述
最小生成树问题是实际生产生活中十分重要的一类问题。假设需要在n个城市之间建立通信联络网,则连通n个城市只需要n-1条线路。这时,自然需要考虑这样一个问题,即如何在最节省经费的前提下建立这个通信网。
可以用连通网来表示n个城市以及n个城市之间可能设置的通信线路,其中网的顶点表示城市,边表示两个城市之间的线路,赋于边的权值表示相应的代价。对于n个顶点的连通网可以建立许多不同的生成树,每一棵生成树都可以是一个通信网。现在,需要选择一棵生成树,使总的耗费最小。这个问题就是构造连通网的最小代价生成树,简称最小生成树。一棵生成树的代价就是树上各边的代价之和。
而在常用的最小生成树构造算法中,普里姆(Prim)算法是一种非常常用的算法。以下是其算法的大致结构:
在本题中,读入一个无向图的邻接矩阵(即数组表示),建立无向图并按照以上描述中的算法建立最小生成树,并输出最小生成树的代价。
输入
输入的第一行包含一个正整数n,表示图中共有n个顶点。其中n不超过50。
以后的n行中每行有n个用空格隔开的整数,对于第i行的第j个整数,如果不为0,则表示第i个顶点和第j个顶点有直接连接且代价为相应的值,0表示没有直接连接。当i和j相等的时候,保证对应的整数为0。
输入保证邻接矩阵为对称矩阵,即输入的图一定是无向图,且保证图中只有一个连通分量。
输出
只有一个整数,即最小生成树的总代价。请注意行尾输出换行。
样例输入
40 2 4 02 0 3 54 3 0 10 5 1 0
样例输出
6
提示
在本题中,需要掌握图的深度优先遍历的方法,并需要掌握无向图的连通性问题的本质。通过求出无向图的连通分量和对应的生成树,应该能够对图的连通性建立更加直观和清晰的概念。
#include#define inf 99999999int e[55][55],dis[55],book[55];int main(){ int n,sum,count,min,u; int i,j,a; while(scanf("%d",&n)!=EOF) { count = sum = 0; for(i = 1; i <= n; i ++) for(j = 1; j <= n; j ++) { if(i == j) e[i][j] = 0; else e[i][j] = inf; } for(i = 1; i <= n; i ++) book[i] = 0; for(i = 1; i <= n; i ++) { for(j = 1; j <= n; j ++) { scanf("%d",&a); if(a == 0&&i!=j) e[i][j] = inf; else e[i][j] = a; } } for(i = 1; i <= n; i ++) dis[i] = e[1][i]; count ++; book[1] = 1; while(count < n) { min = inf; for(i = 1; i <= n; i ++) { if(!book[i]&&dis[i] < min) { min = dis[i]; u = i; } } book[u] = 1; sum = sum + dis[u]; count ++; for(i = 1; i <= n; i ++) { if(!book[i]&&dis[i] > e[u][i]) dis[i] = e[u][i]; } } printf("%d\n",sum); } return 0;}
问题 E: 算法7-16:弗洛伊德最短路径算法
时间限制: 1 Sec 内存限制: 32 MB 提交: 20 解决: 9 [ ][ ][ ]题目描述
在带权有向图G中,求G中的任意一对顶点间的最短路径问题,也是十分常见的一种问题。
解决这个问题的一个方法是执行n次迪杰斯特拉算法,这样就可以求出每一对顶点间的最短路径,执行的时间复杂度为O(n3)。
而另一种算法是由弗洛伊德提出的,时间复杂度同样是O(n3),但算法的形式简单很多。
可以将弗洛伊德算法描述如下:
在本题中,读入一个有向图的带权邻接矩阵(即数组表示),建立有向图并按照以上描述中的算法求出每一对顶点间的最短路径长度。
输入
输入的第一行包含1个正整数n,表示图中共有n个顶点。其中n不超过50。
以后的n行中每行有n个用空格隔开的整数。对于第i行的第j个整数,如果大于0,则表示第i个顶点有指向第j个顶点的有向边,且权值为对应的整数值;如果这个整数为0,则表示没有i指向j的有向边。当i和j相等的时候,保证对应的整数为0。
输出
共有n行,每行有n个整数,表示源点至每一个顶点的最短路径长度。如果不存在从源点至相应顶点的路径,输出-1。对于某个顶点到其本身的最短路径长度,输出0。
请在每个整数后输出一个空格,并请注意行尾输出换行。
样例输入
40 3 0 10 0 4 02 0 0 00 0 1 0
样例输出
0 3 2 1 6 0 4 7 2 5 0 3 3 6 1 0
提示
在本题中,需要按照题目描述中的算法完成弗洛伊德算法,并在计算最短路径的过程中将每个顶点是否可达记录下来,直到求出每一对顶点的最短路径之后,算法才能够结束。
相对于迪杰斯特拉算法,弗洛伊德算法的形式更为简单。通过一个三重循环,弗洛伊德算法可以方便的求出每一对顶点间的最短距离。
另外需要注意的是,为了更方便的表示顶点间的不可达状态,可以使用一个十分大的值作为标记。而在题目描述中的算法示例使用了另外一个三维数组对其进行表示,这使原本的O(n3)时间复杂度增长到了O(n4),这也是需要自行修改的部分。
#include#define inf 999999int e[55][55];int main(){ int n,a; int i,j,k; while(scanf("%d",&n)!=EOF) { for(i = 1; i <= n; i ++) for(j = 1; j <= n; j ++) { if(i == j) e[i][j] = 0; else e[i][j] = inf; } for(i = 1; i <= n; i ++) { for(j = 1; j <= n; j ++) { scanf("%d",&a); if(i != j&&a == 0) e[i][j] = inf; else e[i][j] = a; } } for(k = 1; k <= n; k ++) { for(i = 1; i <= n; i ++) { for(j = 1; j <= n; j ++) { if(e[i][j] > e[i][k] + e[k][j]) e[i][j] = e[i][k] + e[k][j]; } } } for(i = 1; i <= n; i ++) { for(j = 1; j <= n; j ++) { if(e[i][j] == inf&& i!=j) printf("-1 "); else printf("%d ",e[i][j]); } printf("\n"); } } return 0;}