最小生成树

探讨了在完全图中,如何构造一个最小生成树,使得指定数量的顶点度数为1。提供了完整的解题思路及代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最小生成树

                                                                             Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
                                                                                                           Total Submission(s): 113    Accepted Submission(s): 11


Problem Description
ap最近学习了最小生成树树之后,向你提了这么一个问题:给一个n个节点的完全图,节点编号从1-n, 如果限制这个完全图的生成树中k个点的度数为1,那么还能否构造出最小生成树呢?
 

Input
第一行一个正整数T,代表有T组数据
每组数据,第一个两个正整数n, k (2 <= n <= 300, 0 <= k <= n)
接下来n*(n-1)/2行,每行三个正整数ui, vi, wi, 代表节点ui和节点vi之间有一条权值为wi的边 (0 <= wi <= 10000)
最后一行, k个互不相同的数fi, 代表限制度数为1的节点的编号
输入保证给出的是完全图
 

Output
每组数据,若符合条件的最小生成树存在,输出该最小生成树所有边权之和; 若不存在, 输出-1
 

Sample Input
3 3 0 1 2 1 2 3 2 3 1 3 3 1 1 2 1 2 3 2 3 1 3 2 3 3 1 2 1 2 3 2 3 1 3 1 2 3
 

Sample Output
3 4 -1
 

解题思路:若度为一的点是所有点,当n等于2的时候,答案就是唯一边权,否则就是形不成最小生成树,若度为一的点小于顶点数,那么先将度不为一的点用最小边连起来,再找和度为一的点最近的边(边的连点都为度为一的点是不能取的)


#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <vector>
#include <set>
#include <bitset>
#include <stack>
#include <map>
#include <climits>
#include <functional>

using namespace std;

#define LL long long
const int INF=0x3f3f3f3f;

struct node
{
    int s,e,w,flag;
}x[90000];

int k,a[350];
int f[350],n;
int sum[350];

int Find(int x)
{
    if(f[x]==x) return x;
    return f[x]=Find(f[x]);
}

bool cmp(node a,node b)
{
    return a.w<b.w;
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int p;
        memset(a,0,sizeof a);
        scanf("%d %d",&n,&k);
        for(int i=1;i<=(n-1)*n/2;i++)
            scanf("%d %d %d",&x[i].s,&x[i].e,&x[i].w),x[i].flag=0;
        for(int i=1;i<=k;i++)
            scanf("%d",&p),a[p]=1;
        if(n==k)
        {
            if(n==2) {printf("%d\n",x[1].w);continue;}
            else printf("-1\n");
        }
        for(int i=1;i<=n;i++) f[i]=i;
        memset(sum,0,sizeof sum);
        sort(x+1,x+1+n*(n-1)/2,cmp);
        int cnt=0,ans=0;
        for(int i=1;i<=(n-1)*n/2;i++)
        {
            if(a[x[i].s]) continue;
            if(a[x[i].e]) continue;
            int ss=Find(x[i].s),ee=Find(x[i].e);
            if(ss!=ee)
            {
                f[ss]=ee;
                cnt++;
                ans+=x[i].w;
                x[i].flag=1;
            }
            if(cnt==n-1) break;
        }
        for(int i=1;i<=(n-1)*n/2;i++)
        {
            if(cnt==n-1) break;
            if(x[i].flag) continue;
            if(a[x[i].s]&&a[x[i].e]) continue;
            if(a[x[i].s]&&sum[x[i].s]) continue;
            if(a[x[i].e]&&sum[x[i].e]) continue;
            int ss=Find(x[i].s),ee=Find(x[i].e);
            if(ss!=ee)
            {
                if(a[x[i].s]) sum[x[i].s]++;
                else if(a[x[i].e]) sum[x[i].e]++;
                f[ss]=ee;
                cnt++;
                ans+=x[i].w;
            }
            if(cnt==n-1) break;
        }
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值