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

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\n") ;
                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\n",edge[i].u,edge[i].v) ;
    return 0;
}

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\n",maxx) ;
        if(cnt != n - 1)
        {
            printf("-1\n") ;
            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\n",cnt) ;
            if(cnt != n - 1) continue ;
            MIN = min(MIN,maxx) ;
        }
        printf("%d\n",MIN) ;
    }

    return 0;
}

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!\n") ;
        else printf("%d\n", MST) ;
    }
    return 0 ;
}

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\n",tot) ;
    }
    return 0;
}

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\n",tot) ;
    return 0;
}

原文地址:https://www.cnblogs.com/wifePI/p/12199237.html

时间: 2024-11-05 22:43:43

寒假集训第二天---最小生成树题解的相关文章

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

补题什么的待填坑... A - Generous Kefa (语法基础) 直接开桶看看有没有超过三个的,因为题目明确提出没有气球也是可以的 代码 #include <bits/stdc++.h> using namespace std; int bk[123213]; int main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int n,k; cin>>n>>k; string a; cin>&g

寒假集训第二场

整理人:贺振原 A - Generous Kefa (1) 马鸿儒 (2) 朱远迪 B - Godsend (1) 马鸿儒 (2) 朱远迪 C - Leha and Function D - Leha and another game about graph E - New Year and Ancient Prophecy F - New Year and Days (1) 马鸿儒 (2) 朱远迪 G - New Year and Domino H - New Year and Old Pro

CSU-ACM寒假集训选拔-入门题

CSU-ACM寒假集训选拔-入门题 仅选择部分有价值的题 J(2165): 时间旅行 Description 假设 Bobo 位于时间轴(数轴)上 t0 点,他要使用时间机器回到区间 (0,?h] 中. 当 Bobo 位于时间轴上 t 点,同时时间机器有 c 单位燃料时,他可以选择一个满足 \(\lceil\frac{x}{h}\rceil\leq c\) 的非负整数 x, 那么时间机器会在 [0,?x]中随机整数 y,使 Bobo 回到 (t???y) 点,同时消耗 y 单位燃料. (其中 ?

POJ 2349 Arctic Network 最小生成树题解

本题也是使用Prime和Kruskal都可以的最小生成树的题解. 本题一点新意就是:需要除去最大的S-1个距离,因为可以使用卫星覆盖这些距离. 技巧:建图建有向图,速度快点,不用计算两边. 这里使用Prime,因为是稠密图. #include <stdio.h> #include <math.h> #include <stdlib.h> #include <string.h> #include <limits.h> #include <al

洛谷2018寒假集训tg第二次比赛第二题Princess Principal题解

这算不算泄题啊...被kkk发现会咕咕咕吧. 题目大意:给定一个数列a,与常数n,m,k然后有m个询问,每个询问给定l,r.问在a[l]到a[r]中最少分成几段,使每段的和不超过k,如果无解,输出Chtholly 样例: input: 5 5 72 3 2 3 43 34 45 51 52 4 output: 11122 解答: 首先观察数据范围,n<=1e+6 可能的复杂度为O(mlogn).暴力能搞30分吧. 其实这题还是很妙的.我们先考虑暴力:对于[L,R],设个res,对[l,r]从左往

寒假集训日志(二)——最小生成树,拓扑排序,欧拉回路,连通路

今天学的内容挺多的. (一)首先说最小生成树,两种算法: 1.Kruskal算法( 将边排序,然后再选,关键在于检查是否连通,使用并查集) 2.Prim算法(使用点集,有点类似与最短路的算法) 第一题是并查集算法的使用: A - The Suspects Time Limit:1000MS     Memory Limit:20000KB     64bit IO Format:%I64d & %I64u Submit Status Description 严重急性呼吸系统综合症( SARS),

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

Watering the Fields 时间限制: 1 Sec  内存限制: 64 MB提交: 26  解决: 10[提交][状态][讨论版] 题目描述 Due to a lack of rain, Farmer John wants to build an irrigation system to send water between his N fields (1 <= N <= 2000). Each field i is described by a distinct point (x

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

Gym - 100015C City Driving 题意:给你一个n个点的树再加上一条边,求询问的两个点的最短路 题解:去掉形成的环中的一条边,这样就变成了一个普通的求树上最短路,求得A1,然后求u到删掉边的u,v到删掉边的v,再加上删掉边的权值,求得A2,求v到删掉边的u,u到删掉边的v,再加上删掉边的权值w,求得A3,取最小值即可 我用的是在线倍增,为什么不用tarjan和rmq,我和他们不太熟啊 多组输入,vector忘记初始化了,爷哭了 代?? #include <bits/stdc+

寒假作业第二组P&amp;&amp;Q&amp;&amp;R题解

P的题意是有M份作业,这些作业有不同的截止日期,超过截止日期完成,不同的作业有不同的罚分,求如何完成罚分最低. 首先,从截止日期最长的那个作业到截止日期,这些天数是固定的,所做的就是把这些作业填进这些天.很明显的贪心,把作业按从大到小罚分排序,罚分截止日期越近的放在前面,然后通过枚举把作业往时间轴里放,知道这些天数被填满. 1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #includ