对于这道题,稍有常识的人都知道(逃是一道裸的最小生成树版题,那么就是运用到某K算法,下面简述一下。
首先定义一下生成树的概念,指的是对于一个有n个点的图,删去若干条边,使得整张图为一个只有n-1条边的联通块,而最小生成树则取生成树中的极小,既边权和为最小。
对于kruscal算法步骤如下
1.对于边的长度按照单调递增的要求排序
const int Maxn = 10010; struct st { int u, v, w; } edge[Maxn] inline int comp (const st & a, const st & b){ return a.w < b.w ; } inline void kruscal () { sort(edge+1, edge+1+m, comp); ....... }
2.贪心地从小到大选取边。对于每一个边如果连接前该边两个端点已经联通,就不选择,否则将他们联通并将该边边权计入总和,一直到加入n-1条边。
const int MAxn = 110; int ans,fa[MAxn]; int find (int x) { return fa[x] == x?x:fa[x] = find(x); //路径压缩 } inline void clear () { for (int i = 1; i < = n; ++i ) fa[i] = i; //初始化fa数组 } inline void kruscal () { ...... int cnt = 0; for(int i=1; i < = m; ++i){ int uu = find (edge[i].u); int vv = find (edge[i].v); if(uu == vv) continue; ans += edge[i].w; fa[uu] = vv; if( ++cnt == n-1 ) break; } }
最后输出这个总和就好了,以上就是k算法的简介。
但是这道题如果就是完全的版题,那就没有存在的意义了,首先我们注意到存边时我们对于无向图只存边一次,如果将整个矩阵存图,那么无疑会导致错误,通过观察易得,对于右上角的矩阵来说,y都大于x,易得存图代码。
int m = 0, val; inline void add_edge ( int u, int v, int w ) { edge[++m].u = u; edge[m].v = v; edge[m].w = w; } int main(){ scanf ( "%d", &n ); for ( int i = 1; i <= n; ++i ) for ( int j = 1; j <= n; ++j ) { scanf ( "%d", &val ) ; if ( j < i ) add_edge ( i, j, val ) ; }
最后附上本题完整ac代码以及ac用时
#include<bits/stdc++.h> using namespace std; const int Maxn=110; const int MAxn=10010; int n,val,cnt=0; int tot=0,ans=0,fa[Maxn]; struct st{ int u,v,w; }edge[MAxn]; inline void add_edge(int u,int v,int w){ edge[++cnt].u=u; edge[cnt].v=v; edge[cnt].w=w; } int comp(const st &a,const st &b){ return a.w<b.w; } int find(int x){ return fa[x]==x?x:fa[x]=find(fa[x]); } inline void kruscal(){ sort(edge+1,edge+1+cnt,comp); for(int i=1;i<=cnt;++i){ int uu=find(edge[i].u); int vv=find(edge[i].v); if(uu==vv) continue; ans+=edge[i].w; fa[uu]=vv; if(++tot==n-1) break; } } int main(){ scanf("%d",&n); for(int i=1;i<=n;++i) fa[i]=i; for(int i=1;i<=n;++i) for(int j=1;j<=n;++j){ scanf("%d",&val); if(j>i) add_edge(i,j,val); } kruscal(); printf("%d",ans); return 0; }
不要介意码分的转化,博主现在在变码分,见谅
最后,希望大家指出我博客错误,OI就是一个共同学习的过程