【最小生成树】口袋的天空
kruskal是怎么都行,但是prim需要先建好最小树,然后又减去最大的几条边

#include<iostream> #include<algorithm> using namespace std; const int N = 5e3+10; const int M = 2e5+10; struct edge { int u, v, w; } e[M]; //MergeFindSet int fa[N]; int Get(int x) { if(fa[x] == x) return x; return fa[x] = Get(fa[x]); } inline void MergeFindSetInit(int &n) { for(int i = 1; i<= n; i++) fa[i] = i; } bool merge(int u, int v) { int fu = Get(u); int fv = Get(v); if(fu != fv) { fa[fv] = fu; return true; } else return false; } //Kruskal bool Cmp(edge a, edge b) { return a.w < b.w; } int Kruskal(int n, int m, int k) { int sum = 0; MergeFindSetInit(n); sort(e+1, e+m+1, Cmp); int cnt = 0; for(int i = 1; i <= m; i++) { if(cnt == n-k) break; if(merge(e[i].u, e[i].v)) { cnt++; sum += e[i].w; } } if(cnt == n-k) return sum; return -1; } //in inline void In(int &n, int &m, int &k) { cin >> n >> m >> k; for(int i = 1; i <= m; i++) { cin >> e[i].u >> e[i].v >> e[i].w; } } int main() { int n, m, k; In(n, m, k); int ans = Kruskal(n, m, k); if(ans == -1) cout <<"No Answer"; else cout << ans << endl; return 0; }
直接prim不行的数据
4 4 2
1 2 1
3 4 1
1 3 100
2 4 100
但是先建树是可行的,观摩别人代码:

#include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <queue> #include <vector> #include <algorithm> using namespace std; struct EDGE { int out,len,nxt; } edge[20005]; struct cmp1 { bool operator () (EDGE &a,EDGE &b) { return a.len > b.len; } }; struct cmp2 { bool operator () (EDGE &a,EDGE &b) { return a.len < b.len; } }; priority_queue <int,vector<EDGE>,cmp1> que; priority_queue <int,vector<EDGE>,cmp2> Ans; int n,m,d,tot,ans,cnt; int head[1005],marks[1005],mark[1005],dis[1005],vis[1005]; void dfs(int pos); void prim(int sta); void dfs(int pos) { marks[pos] = cnt; for(int i = head[pos]; i != 0; i = edge[i].nxt) { if(marks[edge[i].out] != 0) continue; dfs(edge[i].out); } } void prim(int sta) { memset(dis,127,sizeof(dis)); memset(vis,0,sizeof(vis)); dis[sta] = 0; EDGE cc; cc.out = sta,cc.len = 0; que.push(cc); while(!que.empty()) { int u = que.top().out;//当然使用优先队列来求生成树啦 nlogn que.pop(); if(vis[u] == 1) continue; vis[u] = 1; for(int i = head[u]; i != 0; i = edge[i].nxt) { int v = edge[i].out,len = edge[i].len; if(vis[v] == 0 && dis[v] > len) { dis[v] = len; que.push((EDGE) { v,dis[v],0 }); } } } for(int i = 1; i <= n; i ++) { if(marks[i] == marks[sta]) { ans += dis[i]; Ans.push((EDGE) { 0,dis[i],0 }); } } } int main () { //freopen("1195.in","r",stdin); //freopen("1195.out","w",stdout); scanf("%d%d%d",&n,&m,&d); for(int i = 1; i <= m; i ++) { int a,b,c; scanf("%d%d%d",&a,&b,&c); edge[++ tot].nxt = head[a],head[a] = tot,edge[tot].out = b,edge[tot].len = c; edge[++ tot].nxt = head[b],head[b] = tot,edge[tot].out = a,edge[tot].len = c; } for(int i = 1; i <= n; i ++) { if(marks[i] != 0) continue; cnt ++; dfs(i); }//DFS求联通块个数,顺便把每个联通块标号为cnt if(cnt > d) { printf("No Answer"); return 0; } for(int i = 1; i <= n; i ++) { if(mark[marks[i]] == 0) //判断当前联通块是否已建树 { prim(i); mark[marks[i]] = 1; } }//prim算法求最小生成树 for(int i = 1; i <= d - cnt; i ++) { if(!Ans.empty()) //把最大的几条边推出 { ans -= Ans.top().len; Ans.pop(); } else //当发现无边可出却还不能连成k个联通块时,退出 { printf("No Answer"); return 0; } } printf("%d",ans); return 0; }