zoukankan      html  css  js  c++  java
  • 相框

    https://loj.ac/problem/10111

    题目描述

      给出一张图,可以有两种操作:(①)把一个点拆成若干点,连在这个点上的边可以任意连到拆成的点上。(②)将两条边自由端熔融成一个点。求最少的操作数使得这张图变成一个简单环。

    思路

      还是一样,我们从最简单开始,如果图是一个连通图,那么我们考虑最后的简单环每个点的度均为(2),所以我们计奇数点的个数为(cnt1),度大于(2)的点的个数为(cnt2),那么对于(cnt2)个点,我们都需要进行至少一次的熔断,接下来思考能如何进行最少的熔融次数使图变成一个简单环,由于奇数的存在,所以熔断之后必定仍然存在奇数点,因此以最少的次数消去奇数点至少要(frac{cnt}{2})次(奇数点个数必定为偶数),答案即为(cnt2+frac{cnt1}{2})

      再复杂化,如果是由多个连通块构成,那么我们先要考虑把每个连通块都操作成一条链,再通过熔融操作连接起来,对于每个一个连通块,我们进行分类讨论:

      (①)如果存在奇数点,那么就不用管,显然可以通过(cnt2+frac{cnt1-2}{2})变为一条链,再加上分摊到每个连通块的一个熔融连接操作,不改变操作次数。

      (②)如果不存在奇数点,就需要造出两个度为(1)的点,(cnt1)要加(2);

       (一)如果存在度大于(2)的点,可以在熔断的时候分割成两个度为(1)的点

       (二)如果不存在度大于(2)的点,就需要额外一次操作造出这样一个节点来完成分割,(cnt2)(1)

      剩下就是一些细节问题,由于点是可以乱搞的,重要的是边,所以孤立点不用考虑。判连通性时用并查集即可,注意初始赋值不是(n),因为有(0)号节点存在,最多点数为(m*2)

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N=2100,M=55000<<1;
    
    struct Edge
    {
        int x,y;
    }e[M];
    
    int fa[M];
    int find(int x)
    {
        return fa[x]==x?x:fa[x]=find(fa[x]);
    }
    void f_union(int x,int y)
    {
        int fx=find(x),fy=find(y);
        if(fx!=fy)fa[fx]=fy;
    }
    
    int read()
    {
        int res=0,w=1;
        char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){res=(res<<3)+(res<<1)+ch-'0';ch=getchar();}
        return res*w;
    }
    
    int deg[M];
    bool flag1[M],flag2[M];
    int main() 
    {
        int n,m;
        n=read();m=read();
        for(int i=1;i<=m*2;i++)
            fa[i]=i;
        for(int i=1;i<=m;i++)
        {
            int x=read(),y=read();
            if(!x)x=++n;
            if(!y)y=++n;
            e[i].x=x;e[i].y=y;
            deg[x]++;deg[y]++;
            f_union(x,y);
        }
        int sum=0; 
        int cnt1=0,cnt2=0;
        for(int i=1;i<=n;i++)
        {
            if(deg[i]==0)continue ;
            if(find(i)==i)sum++;
            if(deg[i]&1)
            {
                cnt1++;
                flag1[find(i)]=1;
            }
            if(deg[i]>2)
            {
                cnt2++;
                flag2[find(i)]=1;
            }
        }
        if(sum>1)
            for(int i=1;i<=n;i++)
                if(deg[i]&&find(i)==i&&!flag1[i])
                {
                    cnt1+=2;
                    if(!flag2[i])++cnt2;
                }
        printf("%d",cnt2+cnt1/2);
        return 0;
    }
    
  • 相关阅读:
    注册验证
    翻页加输入框
    php面向对象
    封装数据库
    浅谈BFC和haslayout
    总结JS面向对象
    总结(JS)
    转载6
    总结(5.30)
    总结(5.29)
  • 原文地址:https://www.cnblogs.com/fangbozhen/p/11752838.html
Copyright © 2011-2022 走看看