寒假集训第二天---最小生成树题解

Posted wifepi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了寒假集训第二天---最小生成树题解相关的知识,希望对你有一定的参考价值。

CodeForces - 606D Lazy Student

传送门

题目大意:m条边,每条边给权值和是否在最小生成树里,问能否构造出一个图满足边

思路:排序,对可行的边与1相连,对于不可行的边其它已连结点相连,直到连完或者不满足条件

卡点:对于权值相同的边,在最小生成树里的边的优先级较高

代??

技术图片
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 1e5 + 10 ;
struct node
{
    int val, flag, pos ;
    int u, v ;
}edge[maxn], edge2[maxn] ;
struct Node
{
    int u, v ;
}edd[maxn], edd2[maxn];
vector<int> ve[maxn] ;
bool cmp(node x, node y)
{
    if(x.val == y.val) return x.flag > y.flag ;
    return  x.val < y.val ;
}
bool cmp2(node x , node y)
{
    return x.pos < y.pos ;
}
int main(int argc, char const *argv[])
{
    int n, m ;
    scanf("%d %d",&n,&m) ;
    for(int i = 1 ; i <= n ; ++ i) ve[i].clear(), ve[i].push_back(i) ;
    for(int i = 1, val, flag ; i <= m ; ++ i)
    {
        scanf("%d %d",&edge[i].val,&edge[i].flag) ;
        edge2[i].val = edge[i].val ;
        edge2[i].flag = edge[i].flag ;
        edge[i].pos = i ;
    }
    sort(edge+1,edge+1+m,cmp) ;
    int cnt = 1 ;
    for(int i = 1 ; i <= m ; ++ i)
    {
        if(edge[i].flag == 0)
        {
            int pos = 2 ;
            int ff = 0 ;
            while(pos < cnt)
            {
                int len = ve[pos].size() ;
                if(ve[pos][len-1] < cnt)
                {
                    int x = ve[pos][len-1] + 1 ;
                    ve[pos].push_back(x) ;
                    edge[i].u = pos ;
                    edge[i].v = x ;
                    ff = 1 ;
                    break ;
                }
                else ++ pos ;
            }
            if(ff == 0)
            {
                printf("-1
") ;
                return 0 ;
            }
        }
        else
        {
            ++ cnt ;
            ve[1].push_back(cnt) ;
            edge[i].u = 1 ;
            edge[i].v = cnt ;
        }
    }
    sort(edge+1,edge+1+m,cmp2) ;
    for(int i = 1 ; i <= m ; ++ i) printf("%d %d
",edge[i].u,edge[i].v) ;
    return 0;
}
View Code

UVA 1395 Lazy Student

传送门

题目大意:给一幅图,问所有生成树里该生成树边权值最大的减去权值最小的最小值是多少

思路:按照权值给边排序,每跑完一次最小生成树就去把此时最小的边从边的总集里删去,每次计算出来的最小生成树的差值的最小值即为所求

卡点:要使得最后的值最小,在我选定一条边的时候,最小值一定出现在包括这条边的最小生成树里

代??

技术图片
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAX = 0x3f3f3f3f ;
int fa[128] ;
int cnt = 0 ;
struct node
{
    int u, v, w ;
}edge[5000] ;
int Find(int x)
{
    return x == fa[x] ? fa[x] : fa[x] = Find(fa[x]) ;
}
int merge(int x , int y , int n)
{
    int fx = Find(x) ;
    int fy = Find(y) ;
    if(fx != fy)
    {
        ++ cnt ;
        fa[fx] = fy ;
        return 1 ;
    }
    return 0 ;
}
bool cmp(node x, node y)
{
    return x.w < y.w ;
}
int main(int argc, char const *argv[])
{
    int n , m ;
    int MIN = MAX ;
    while(scanf("%d %d",&n,&m) != EOF)
    {
        if(n == 0 && m == 0) break ;
        cnt = 0 ;
        MIN = MAX ;
        int maxx = 0 ;
        for(int i = 1; i <= m ; ++ i) scanf("%d %d %d",&edge[i].u,&edge[i].v,&edge[i].w) ;
        for(int i = 1 ; i <= n ; ++ i) fa[i] = i ;
        sort(edge+1,edge+1+m,cmp) ;
        for(int i = 1 ; i <= m ; ++ i)
        {
            int flag = merge(edge[i].u,edge[i].v,i) ;
            if(flag) maxx = edge[i].w - edge[1].w ;
        }
        //printf("%d
",maxx) ;
        if(cnt != n - 1)
        {
            printf("-1
") ;
            continue ;
        }
        MIN = maxx ;
        cnt = 0 ;
        for(int i = 2 ; i <= m, m-i+1>=n-1 ; ++ i)
        {
            for(int k = 1 ; k <= n ; ++ k) fa[k] = k ;
            cnt = 0 ;
            for(int j = i ; j <= m ; ++ j)
            {
                int flag = merge(edge[j].u,edge[j].v,j) ;
                if(flag) maxx = edge[j].w - edge[i].w ;

                if(cnt == n - 1) break ;
            }
            //printf("cnt = %d
",cnt) ;
            if(cnt != n - 1) continue ;
            MIN = min(MIN,maxx) ;
        }
        printf("%d
",MIN) ;
    }

    return 0;
}
View Code

POJ 1679 The Unique MST

传送门

题目大意:问最小生成树是否唯一

思路:跑一遍次小生成树,与最小生成树比较权值

卡点:在求次小生成树中求的包括给定边的最小生成树,如果满足条件退出,给wa,跑完次小生成树再去判断,A了,我太菜了不太明白

代??

技术图片
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std ;

const int maxn = 1000 + 10, maxe = 1000 * 1000 / 2 + 5, INF = 0x3f3f3f3f ;
int n, m, fa[maxn], Max[maxn][maxn] ;
struct Edge
{
    int u, v, w ;
    bool vis ;
}edge[maxe] ;
vector<int> G[maxn] ;

bool cmp(const Edge &a, const Edge &b)
{
    return a.w < b.w ;
}

void Init()
{
    for(int i = 1 ; i <= n ; ++ i)
    {
        G[i].clear() ;
        G[i].push_back(i) ;
        fa[i] = i ;
    }
}

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

int Kruskal()
{
    sort(edge + 1, edge + 1 + m, cmp) ;
    Init() ;
    int ans = 0, cnt = 0 ;
    for(int i = 1 ; i <= m ; ++ i)
    {
        if(cnt == n - 1) break ;
        int fx = Find(edge[i].u), fy = Find(edge[i].v) ;
        if(fx != fy)
        {
            cnt ++ ;
            edge[i].vis = true ;
            ans += edge[i].w ;
            int len_fx = G[fx].size(), len_fy = G[fy].size() ;
            for(int j = 0 ; j < len_fx ; ++ j)
                for(int k = 0 ; k < len_fy ; ++ k)
                    Max[G[fx][j]][G[fy][k]] = Max[G[fy][k]][G[fx][j]] = edge[i].w ;
            fa[fx] = fy ;
            for(int j = 0 ; j < len_fx ; ++ j)
                G[fy].push_back(G[fx][j]) ;
        }
    }
    return ans ;
}

int Second_Kruskal(int MST)
{
    int ans = INF ;
    for(int i = 1 ; i <= m ; ++ i)
        if(!edge[i].vis)
            ans = min(ans, MST + edge[i].w - Max[edge[i].u][edge[i].v]) ;
    return ans ;
}

int main()
{
    int t ;
    scanf("%d", &t) ;
    while(t --)
    {
        scanf("%d %d", &n, &m) ;
        for(int i = 1 ; i <= m ; ++ i)
        {
            scanf("%d %d %d", &edge[i].u, &edge[i].v, &edge[i].w) ;
            edge[i].vis = false ;
        }
        int MST = Kruskal() ;
        int Second_MST = Second_Kruskal(MST) ;
        if(Second_MST == MST) printf("Not Unique!
") ;
        else printf("%d
", MST) ;
    }
    return 0 ;
}
View Code

HUD 4463 Outlets

传送门

题目大意:求有固定边的最小生成树

思路:把固定边权值赋为0,再去跑最小生成树即可

卡点:

代??

技术图片
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 2e3 ;
int fa[55] ;
int n, m, p, q ;
int mp[55][5] ;
double tot = 0 ;
struct node
{
    int u, v ;
    double w ;
}edge[maxn] ;
int Find(int x)
{
    return fa[x] == x ? fa[x] : fa[x] = Find(fa[x]) ;
}
void merge(int x, int y, int n)
{
    int fx = Find(x) ;
    int fy = Find(y) ;
    if(fx != fy)
    {
        fa[fx] = fy ;
        tot += edge[n].w ;
    }
}
bool cmp(node x , node y)
{
    return x.w < y.w ;
}
int main(int argc, char const *argv[])
{
    while(scanf("%d",&n) != EOF)
    {
        if(n == 0) break ;
        for(int i = 1 ; i <= n ; ++ i) fa[i] = i ;
        tot = 0.0 ;
        scanf("%d %d",&p,&q) ;
        for(int i = 1, u, v ; i <= n ; ++ i)
        {
            scanf("%d %d",&mp[i][0],&mp[i][1]) ;
        }
        int cnt = 0 ;
        if(p > q) swap(p,q) ;
        for(int i = 1 ; i <= n ; ++ i)
        {
            for(int j = i + 1 ; j <= n ; ++ j)
            {
                edge[++ cnt].u = i ;
                edge[cnt].v = j ;
                edge[cnt].w = 1.0 * sqrt((mp[i][0] - mp[j][0])*(mp[i][0] - mp[j][0]) + (mp[i][1] - mp[j][1])*(mp[i][1] - mp[j][1])) ;
                if(i == p && j == q) tot = edge[cnt].w, edge[cnt].w = 0.0 ;
            }
        }
        sort(edge+1,edge+1+cnt,cmp) ;
        for(int i = 1 ; i <= cnt ; ++ i)
        {
            merge(edge[i].u,edge[i].v,i) ;
        }
        printf("%.2lf
",tot) ;
    }
    return 0;
}
View Code

CodeForces - 1108F MST Unification

传送门

题目大意:n个顶点,至少n-1条边,可以对边的权值进行加1操作,这样算一次操作,问最少要几次操作才能使得最小生成树唯一

思路:跑kruskal的过程中,对于权值相等的边,计算出矛盾边的数量,累加矛盾边就是最后答案。矛盾边定义为加了x边不能加其他权值相等的边,这些边姑且称作矛盾边。但要注意,权值相等不一定矛盾。

卡点:参考博客,巨清晰

代??

技术图片
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10 ;
#define ll long long
int n , m ;
struct node
{
    int u, v, w ;
}edge[maxn] ;
int fa[maxn] ;
int Find(int x)
{
    return x == fa[x] ? fa[x] : fa[x] = Find(fa[x]) ;
}
bool cmp(node x , node y)
{
    return x.w < y.w ;
}
int main(int argc, char const *argv[])
{
    scanf("%d %d",&n,&m) ;
    for(int i = 1, u, v, w ; i <= m ; ++ i)
    {
        scanf("%d %d %d",&edge[i].u,&edge[i].v,&edge[i].w) ;
    }
    sort(edge+1,edge+1+m,cmp) ;
    for(int i = 1 ; i <= n ; ++ i) fa[i] = i ;
    int tot = 0 ;
    for(int i = 1 ; i <= m ;)
    {
        int cnt = 0 ;
        int ed = i ;
        while(edge[ed].w == edge[i].w) ed ++ ;

        for(int j = i ; j < ed ; ++ j)
        {
            int fx = Find(edge[j].u), fy = Find(edge[j].v) ;
            if(fx != fy) cnt ++ ;
        }
        for(int j = i ; j < ed ; ++ j)
        {
            int fx = Find(edge[j].u), fy = Find(edge[j].v) ;
            if(fx != fy) fa[fx] = fy, cnt -- ;
        }
        tot += cnt ;
        i = ed ;
    }
    printf("%d
",tot) ;
    return 0;
}
View Code

以上是关于寒假集训第二天---最小生成树题解的主要内容,如果未能解决你的问题,请参考以下文章

(寒假集训)Watering the Fields (最小生成树)

寒假集训第六天---LCA题解

2022牛客寒假算法基础集训营 4 全部题解

2022牛客寒假算法基础集训营 4 全部题解

集训队寒假集训第二场补题题解

2022牛客寒假算法基础集训营4 ABCDEFGHIJKL