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;    
}
原文地址:https://www.cnblogs.com/staginner/p/2648919.html