此题为最小生成树的基础题,一开始用Prim算法发现超时,当时想的是把已联通的城市当作一个城市,但发现用了太多的时间去处理。最后发现适合用kruskal求解,利用并查集的思想,对于n个点,全部连通需要ee=n-1条边,对于那些已联通的点,输入时进行处理,把他们并起来,使得在使用kruskal算法进行处理之前,那些已联通的点可视为一个点。同时,每次处理了一条边,ee--。最后,当ee==0时,得到解,否则无解。
	还需要注意的是,利用上述的解法,一直超时。最后才知道,排序时需要用快排,最后才勉强过了。![在这里插入图片描述](https://img-blog.csdnimg.cn/20190714093155682.PNG)
#include <cstdio>//kruskal,部分联通
#include <algorithm>
using namespace std;
int n,m,k,ee,ans;
struct node
{
    int s,e,w;
};
node edge[25005];
int pre[505];
int fin(int x)
{
    int r=x;
    while(r!=pre[r])
        r=pre[r];
    int i,j;
    i=x;
    while(i!=r)
    {
        j=pre[i];
        pre[i]=r;
        i=j;
    }
    return r;
}
/*bool cmp(node a,node b)
{
    return a.w<b.w;
}*/
int cmp(const void *a,const void *b){
	return ((node *)a)->w - ((node *)b)->w;
}
void kruskal()
{
    int f=0;
    for(int i=1;i<=m;i++)
    {
        int a=fin(edge[i].s);
        int b=fin(edge[i].e);
        if(a!=b)
        {
            ans+=edge[i].w;
            pre[b]=a;
            ee--;
        }
        if(ee==0)
        {
            f=1;
            printf("%d\n",ans);
            break;
        }
    }
    if(f==0)
        printf("-1\n");
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d",&n,&m,&k);
        for(int i=1;i<=n;i++)
            pre[i]=i;
        //memset(edge,0,sizeof(edge));
        int p,q,c;
        ee=n-1;
        for(int i=1;i<=m;i++)
            scanf("%d%d%d",&edge[i].s,&edge[i].e,&edge[i].w);
        int x;
        for(int i=1;i<=k;i++)
        {
            scanf("%d",&x);
            scanf("%d",&p);
            c=fin(p);//已联通的城市取公共的父亲,则只需要连接其中一个点
            for(int j=1;j<x;j++)
            {
                scanf("%d",&p);
                q=fin(p);
                if(q!=c)
                {
                    ee--;
                    pre[q]=c;
                }
            }
        }
        //sort(edge+1,edge+1+m,cmp);
        qsort(edge+1,m,sizeof(node),cmp);
        ans=0;
        kruskal();
    }
    return 0;
}