最小生成树 HihoCoder-1097、1098、1109(最小生成树算法)
太久没写最小生成树了,快忘光了。这几天回顾了一下
- 最小生成树一·Prim算法
AC G++ 369ms 17MB #include "cstdio" using namespace std; const int INF = 0x3f3f3f3f; int road[1005][1005]; int dis[1005], n, ans; bool vis[1005]; void prim() { int v, mn; for (int i = 1; i <= n; i++) { dis[i] = road[1][i]; } vis[1] = true; for (int i = 1; i < n; i++) { mn = INF; for (int j = 1; j <= n; j++) { if (!vis[j] && dis[j] < mn) { mn = dis[j]; v = j; } } ans += dis[v]; vis[v] = true; for (int j = 1; j <= n; j++) { if (!vis[j] && dis[j] > road[v][j]) { dis[j] = road[v][j]; } /* 这个if改成如下写法就变成Dijkstra算法求最短路了 if (!vis[j] && dis[j] > dis[v] + road[v][j]) { dis[j] = dis[v] + road[v][j]; } */ } } } int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { scanf("%d", &road[i][j]); } } prim(); printf("%d\n", ans); return 0; }
理解了Dijkstra看这个就很容易了,改一下if语句多加一个ans就是了;
- 最小生成树二·Kruscal算法
AC G++ 342ms 17MB #include "bits/stdc++.h" using namespace std; typedef long long LL; const int INF = 0x3f3f3f3f; // 并查集 int pre[100005]; struct Road{ int s, e, v; }road[1000005]; bool cmp(Road n, Road m) { return n.v < m.v; } int find(int id) { if (pre[id] == 0) { return id; } return pre[id] = find(pre[id]); } int main() { int n, m, ans = 0; scanf("%d%d", &n, &m); for (int i = 0; i < m; i++) { scanf("%d%d%d", &road[i].s, &road[i].e, &road[i].v); } sort (road, road + m, cmp); // 用n - 1条边可以联通n个点,所以当n == 1的时候退出循环 for (int i = 0; n != 1; i++) { int s = find(road[i].s); int e = find(road[i].e); if (s != e) { pre[s] = e; n--; ans += road[i].v; } } printf("%d\n", ans); return 0; }
Kruscal算法有点贪心的意思在里面吧,每次取最短的边,当n个点被联通时退出循环
- 最小生成树三·堆优化的Prim算法
AC G++ 436ms 24MB #include "bits/stdc++.h" using namespace std; typedef long long LL; typedef pair<int, int> PII; const int INF = 0x3f3f3f3f; const int MAXN = 1e5 + 5; /* road[i].first 存放以i为顶点的边的另一个顶点 road[i].second 存放以i为顶点的边的长度 */ vector<PII> road[MAXN]; int dis[MAXN]; int ans; bool vis[MAXN]; void prim() { // q表示就目前状态可以花费q.top.first的代价去联通q.top.second这个点 priority_queue<PII, vector<PII>, greater<PII> > q; memset(dis, INF, sizeof(dis)); q.push({0, 1}); while (!q.empty()) { int id = q.top().second; if (vis[id]) { // 如果id已经被联通,直接continue q.pop(); continue; } else { // 否则由于优先列队的排序,q.top.first一定是最小代价 ans += q.top().first; q.pop(); } vis[id] = true; for (auto i : road[id]) { if (i.second < dis[i.first]) { dis[i.first] = i.second; q.push({i.second, i.first}); } } } } int main() { int n, m; int u, v, w; scanf("%d%d", &n, &m); while (m--) { scanf("%d%d%d", &u, &v, &w); road[u].push_back({v, w}); road[v].push_back({u, w}); } prim(); printf("%d\n", ans); return 0; }
果然还是和dijkstra很像啊,仿照dijkstra的堆优化就好了。这种建图的方法倒是比原先用的链式前向星好写多了。