2018山东冬令营 中国石油 森林扩张



1380: 森林扩张

时间限制: 1 Sec   内存限制: 256 MB
提交: 127   解决: 22
[ 提交][ 状态][ 讨论版]

题目描述

小L走进了一片森林。这片森林由n个点和m条边组成,每个点都有一个大小ai。小L不小心在森林里迷路了,于是TA决定给森林添上几条边,把整片森林连成一棵树,就能走出去了,在点i与点j间连一条边需要花费ai+aj的代价。此外,小L还发现每个点上最多只能添一条边。小L想知道能不能走出森林,如果能的话,最小代价是多少。

输入

第一行两个整数n和m,表示森林的点数与边数。接下来一行,n个以空格分隔的整数,表示每个点的大小。接下来m行,每行两个正整数,描述森林的一条边。保证给出的图是一片森林。

输出

假如不能把森林变成一棵树,输出−1,否则输出最小代价。

样例输入

5 2
1 2 5 3 4
1 3
2 4

样例输出

10

提示

在点1与点2间加入一条边,点4与点5间加入一条边,总代价是1 + 2 + 3 + 4 = 10。
对于30%的数据,所有点的点权都相同;
对于100%的数据,0 ≤ m < n ≤ 105, 0 ≤ ai ≤ 109,保证给出的图是一片森林(可能是一棵完整的树)。


刚开始写的时候写炸了,TL WA到怀疑人生,后来发现自己想错了,感觉当时脑子肯定瓦特了。结束后问了问思路,发现自己的确瓦特了,改了改想法,过了。但是忘了写题解了。今天来补上。


用并查集来判断当前可以有多少个集合,单点算是一个集合。 如果要构成一棵树,起码需要n-1条边吧,肯定是从每个集合中选择一个最小的,这样起码可以有ans个点了吧,剩下的就是需要从剩下的点里面找最小的就行,不用管那些连接的边,因为如果用到了那条边,肯定还需要那个点的权值。


int n,m;
int a[100005];
int father[100005];
vector<ll> f[100005];
int find(int x)
{
    if(x!=father[x])
        father[x]=find(father[x]);
    return father[x];
}
void Find(int x,int y)
{
    int fx=find(x);
    int fy=find(y);
    if(fx!=fy)
    {
        father[fx]=fy;
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
        father[i]=i;
    for(int i=0;i<m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        Find(x,y);
    }
    int ans=0;
    for(int i=1;i<=n;i++)//计算有多少个集合  如果是单个的点 也算是一个集合 
    {
        f[find(i)].push_back(a[i]);//放到集合里面    
        if(father[i]==i)
            ans++;
    }
    if(ans==1)//只有一个集合 
    {
        printf("0\n");
        return 0;
    } 
    for(int i=1;i<=n;i++)//将每个集合排序下 
    {
        sort(f[i].begin(),f[i].end());
    }
    long long sum=0;
    for(int i=1;i<=n;i++)//从每个集合中选出来一个最小的 
    {
        if(f[i].size())
            sum=sum+f[i][0];
        for(int j=1;j<f[i].size();j++)
            f[0].push_back(f[i][j]);
    }
    if(f[0].size()<ans-2)//n个集合中 最小还需要n-2个连接点(一个是两个集合的最小值连接,另外一个是最小值连接其他集合) 
    {
        printf("-1\n");
        return 0;
    }
    sort(f[0].begin(),f[0].end());//n个集合需要选择n-2条边  所以还需要选择n-2个点 连接成边 
    for(int j=0;j<ans-2;j++)
        sum=sum+f[0][j];
    printf("%lld\n",sum);
    return 0; 
} 


猜你喜欢

转载自blog.csdn.net/passer__/article/details/79428219
今日推荐