原题链接:http://poj.org/problem?id=1258
周四上了节数据结构终于知道何为最小生成树,晚上回寝,遂得此题!
Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 29613 | Accepted: 11750 |
Description
Input
Output
Sample Input
4 0 4 9 21 4 0 8 17 9 8 0 16 21 17 16 0
Sample Output
28
给你N*N矩阵,表示N个村庄之间的距离。FJ要把N个村庄全都连接起来,求连接的最短距离。(即传说中的最小生成树)
样例分析:
连接方式:1-----------2-----------3----------4
距离: 4 + 8 + 16 = 24
图论之最小生成树问题。。。下面的是Prime算法复杂度O(n*n)
最小生成树算法思想:
假设N=(V,E) 是连通网,TE是N上最小生成树中边的集合,算法从U={vk},TE={ }开始(即从vk出发求最小生成树,vk∈V)。
重复执行下述操作:
在所有的边(vi,vj)∈E (vi∈U,vj∈V-U)中寻找一条权值最小的边(vi,vj)将其添加到TE中(或打印之),同时把vj添 加到集合U 中 。
反复执行上述操作n-1次(或所有顶点全部加入U时为止)。
通俗的说:1、有一个图N=(V,E),设置一个空集合U,设置路长ans=0。
2、就是先找一个点v1,将v1添加到集合U,再找出和它距离最近的点v2,再把v2添加到集合U,ans加上两点间的距离。
3、再到点集合V-U (属于V,但是不属于U的点)中找出一个离集合U最近的点。将找到的点添加到集合U,ans加上这个最短的距离。
4、如此循环第三步,直到所有的点全到U中。
关于这个动态的PPT,见我传的老师课件:
Accepted | 216K | 16MS | C++ | 1078B |
//AC #include<stdio.h> #include<string.h> int map[105][105]; int lowest[105]; bool vis[105]; int n; int min()//找到离已经连好了的村庄最短距离的村庄的标号 { int i; int m=100000,k=-1; for(i=1;i<=n;i++) { if(!vis[i] && lowest[i]<m) { m=lowest[i];k=i; } } return k; } int main() { int i,j; int ans; while(scanf("%d",&n)!=EOF) { ans=0; memset(vis,false,sizeof(vis)); for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { scanf("%d",&map[i][j]); } } for(i=1;i<=n;i++) lowest[i]=map[1][i]; vis[1]=true; for(i=1;i<n;i++)//找出n-1条最短边 { int k=min(); vis[k]=true;ans+=lowest[k];//找到一个马上标记,并且加上权值 for(j=1;j<=n;j++)//找到一个新的点,马上更新最小距离。 { if(map[k][j]<lowest[j]) { lowest[j]=map[k][j]; } } } printf("%d\n",ans); } return 0; }
1258 | Accepted | 288K | 16MS | C++ | 1178B |
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; const int maxn = 110; int n, m; int ans; struct Edge{ int u,v; int w; }edge[maxn*maxn]; int w[maxn][maxn]; int p[maxn]; bool cmp(Edge a, Edge b) { return a.w < b.w; } int find(int x) { return x == p[x] ? x : p[x] = find(p[x]); } void Kruskal() { for(int i = 0; i < maxn; i++) p[i] = i; sort(edge,edge+m,cmp); for(int i = 0; i < m; i++) { int u = find(edge[i].u); int v = find(edge[i].v); if(u != v) { p[u] = v; ans += edge[i].w; } } } int main() { while(scanf("%d", &n) != EOF) { for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) scanf("%d", &w[i][j]); m = 0; ans = 0; for(int i = 1; i <= n; i++) { for(int j = i+1; j <= n; j++) { edge[m].u = i; edge[m].v = j; edge[m++].w = w[i][j]; } } Kruskal(); printf("%d\n", ans); } return 0; }
1258 | Accepted | 220K | 16MS | C++ | 860B |
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; const int maxn = 110; const int INF = maxn*100000; int w[maxn][maxn]; int d[maxn]; int vis[maxn]; int n; int ans; void Prime() { for(int i = 1; i <= n; i++) d[i] = INF; d[1] = 0; memset(vis, 0, sizeof(vis)); for(int i = 1; i <= n; i++) { int x, m = INF; for(int y = 1; y <= n; y++) if(!vis[y] && d[y] <= m) m = d[x=y]; vis[x] = 1; ans += d[x]; for(int y = 1; y <= n; y++) if(!vis[y]) d[y] = min(d[y], w[x][y]); } } int main() { while(scanf("%d", &n) != EOF) { for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) scanf("%d", &w[i][j]); ans = 0; Prime(); printf("%d\n", ans); } return 0; }