• HDU 1941 Justice League


    HDU_1941

        这个题目说白了就是要删掉一些点,而且被删掉的点两两之间不能有边,留下的点两两之间必须有边,也就是构成一个完全图。

        乍看起来是没啥思路的,不过大概有两条路子可以走,要么先考虑什么样的点一定会留下来,要么就先考虑什么样的点一定会被删掉。想想之后觉得第二条路子还可行一些,于是就开始想吧。

        首先,孤立点是一定可以被删除的(这里以及后面的讨论默认最大度数的点的度数还算比较大),其次好像叶子节点也就是度数为1的点也可以删,再接着好像就不太好讨论了,不过前面这两类点是有共性的,也就是度数比较小,那么是不是先把度数比较小的点删掉就可以了?

        不妨考虑一个度数比较小的点A和一个度数比较大的点B,根据两个点集合的从属关系无非有4种情况:①A和B都被删掉了;②A删掉了,B留下;③A留下,B删掉了,不过这种情况是不可以的,因为假设A是完全图中的点,由于B的度数比A大,即便它和完全图中的所有点都有边,那么还是会有多余的度数,所以一定会和删掉的点之间形成边,所以这种情况不可能发生;④A和B都留下了,这也不可能,因为A的度数比B小,不可能和B一起组成完全图。分析完这四种情况就会比较happy了,因为发现无论如何A都会被删掉。于是我们就可以把点按度数排个序,然后从小度数的点开始删。

        当然这里还有一个问题需要讨论清,如果A和B的度数相等怎么办?这时删掉A也是无所谓的,因为即便A是完全图中的某个点,那么完全图删掉一个点还会是完全图。那么会不会因为删掉A同时留下B(如果B也能删掉的话肯定就对构成完全图没影响了)而导致不会形成完全图呢?

        这时不妨再讨论一下,如果A和B本来是一个完全图中的两个点的话,自然删A和删B的效果是一样的,不会有影响。如果A和B不是一个完全图中的两个点,那么假设A是完全图中的,这时B必然要被删掉,这样B就会有多余的度数和删掉的集合中的点形成边,反过来假设B是完全图中的点,留B删A也是一样的,因此如果A和B不是一个完全图中的两个点,那么本来就不存在合法的方案,于是删A就当玩玩了,反正是无解的,最后判定一下留下的点会不会形成完全图就行了。总而言之,删A不会产生什么不良的后果。

        讨论到这里,算法基本就成型了:

        基本的思路是将一定会被删掉的点删掉,并保证删掉的点之间没有边,最后看留下的点能否构成完全图。

        先把点按度数升序排序,然后从度数较小的开始删,删掉之后更新与之相连的点的度数,并将那些点标记为“必须留下的点”,当扫描到已标记的点的时候就跳过去。最后看留下的点能不能形成完全图。

    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    #define MAXD 50010
    #define MAXM 200010
    #define INF 0x3f3f3f3f
    int N, M, first[MAXD], e, next[MAXM], v[MAXM], dgr[MAXD], isin[MAXD], r[MAXD];
    bool cmp(const int &x, const int &y)
    {
        return dgr[x] < dgr[y];    
    }
    void add(int x, int y)
    {
        v[e] = y;
        next[e] = first[x], first[x] = e ++;    
    }
    void init()
    {
        int i, x, y;
        memset(first, -1, sizeof(first[0]) * (N + 1)), e = 0;
        memset(dgr, 0, sizeof(dgr[0]) * (N + 1));
        for(i = 0; i < M; i ++)
        {
            scanf("%d%d", &x, &y);
            ++ dgr[x], ++ dgr[y];
            add(x, y), add(y, x);
        }
    }
    void solve()
    {
        int i, j, x, cnt = 0, min = INF;
        memset(isin, 0, sizeof(isin[0]) * (N + 1));
        for(i = 1; i <= N; i ++) r[i] = i;
        std::sort(r + 1, r + 1 + N, cmp);
        for(i = 1; i <= N; i ++)
            if(!isin[x = r[i]])
            {
                for(j = first[x]; j != -1; j = next[j])
                    -- dgr[v[j]], isin[v[j]] = 1;
            }
        for(i = 1; i <= N; i ++)
            if(isin[x = r[i]])
                ++ cnt, min = std::min(min, dgr[x]);
        printf("%s\n", cnt == min + 1 ? "Y" : "N");
    }
    int main()
    {
        while(scanf("%d%d", &N, &M), N)
        {
            if(M == 0)
            {
                printf("Y\n");
                continue;    
            }
            init();
            solve();    
        }
        return 0;    
    }
  • 相关阅读:
    关于课内外读物的建议
    c# Aes加解密
    web api 如何通过接收文件流的方式,接收客户端及前端上传的文件
    c# 文件夹权限
    mysql 8创建远程访问用户以及连接mysql速度慢的解决方法
    为什么读书?读书让我们明心见性!
    大部分教程不会告诉你的 12 个 JS 技巧
    nuget包管理nuget服务器发布包时出现请求报错 406 (Not Acceptable)
    Python 实现毫秒级淘宝、京东、天猫等秒杀抢购脚本
    eos的资源和工具列表
  • 原文地址:https://www.cnblogs.com/staginner/p/2648919.html
Copyright © 2020-2023  润新知