原题链接:
一道巧妙的最小生成树。
与最小生成树不同的是,本题因为有打井的存在,所以可以不只有一棵生成树,但是克鲁斯卡尔算法依然十分好用。
首先按照题目要求读入边,虽然题目中给出的是近似邻接矩阵的写法,但是因为要使用克鲁斯卡尔算法,要将其转换成类似邻接表的写法。
对于每一组(i<j)就可以将(i,j,c)这一条边加入。
那么如何处理打井的情况呢?我们可以假想有一个n+1号节点,代表地下水,于是我们可以将所有的(i,n+1,w[i])加入。
这样就有(n-1)*n/2+n条边,然后就可以做做最小生成树了,只是现在有了n+1个点,我们需要n条边了。
#include<cstdio> #include<algorithm> using namespace std; void read(int &y) { y=0;char x=getchar(); while(x<'0'||x>'9') x=getchar(); while(x>='0'&&x<='9') { y=y*10+x-'0'; x=getchar(); } } struct edge { int u,v,w; }e[100505]; bool cmp(edge x,edge y) { return x.w<y.w; } int n,m,tot,c,f[305],ans; void add(int u,int v,int w) { e[++m].u=u; e[m].v=v; e[m].w=w; } int find(int x) { if(x==f[x]) return x; return f[x]=find(f[x]); } int main() { read(n); for(int i=1;i<=n;i++) { read(c); add(i,n+1,c); f[i]=i; } f[n+1]=n+1; for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { read(c); if(i<j) add(i,j,c); } } sort(e+1,e+m+1,cmp); for(int i=1;i<=m;i++) { if(find(e[i].u)==find(e[i].v)) continue; f[find(e[i].u)]=e[i].v; tot++; ans+=e[i].w; if(tot==n) break; } printf("%d",ans); return 0; }