小朋友(洛谷-P3852)

题目描述

幼儿园里有N个小朋友,老师要从中选出来一部分做丢手绢的游戏,可是老师没有想到这么小的孩子里面有些人之间还有矛盾。老师想找出尽量多的小朋友去玩游戏,但是又很头疼,他不想看到找出来玩游戏的小朋友里面还有任何两个人之间存在着矛盾。如果告诉你小朋友之间存在的M对矛盾关系,你能否帮助幼儿园老师计算出他最多可以选出多少个小朋友来做这个丢手绢的游戏?

关于矛盾限制的说明:

如果我们把存在着矛盾的两个小朋友看作是无向图中相连的两个点,那么题目中的数据保证M对矛盾所构成的图中不会有超过3个点的环。(图1符合要求,图2则不符合)

输入输出格式

输入格式:

输入文件的第一行是用空格隔开的两个整数N和M,表示一共有N个小朋友,这些小朋友之间有M对矛盾关系。接下来的M行,每行将有一对整数a和b(用空格隔开),表示小朋友a与小朋友b有矛盾。(小朋友的编号都是从1开始的)

输出格式:

输出一行,包含一个整数,即幼儿园老师最多可以选出来做游戏的人数。

输入输出样例

输入样例#1:

5 6
1 2
3 2
1 3
3 5
3 4
4 5

输出样例#1:

2

说明

幼儿园有6个小朋友,矛盾关系中1 - 2 - 3组成一个环,3 - 4 - 5组成一个环,因此只能在这两个环中分别选一个小朋友,并且不能选择3号小朋友。

对于20%的数据,1 ≤ N ≤ 20

对于40%的数据,1 ≤ N ≤ 50

对于100%的数据,1 ≤ N ≤ 200

对于100%的数据都符合题目中所描述的矛盾限制关系。

思路:可以看出,给出的图是一个弦图,本质上是要求一个最大独立集,套用 MCS 后贪心选取完美消除序列即可

源代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<bitset>
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LL long long
#define Pair pair<int,int>
LL quickPow(LL a,LL b){ LL res=1; while(b){if(b&1)res*=a; a*=a; b>>=1;} return res; }
LL quickModPow(LL a,LL b,LL mod){ LL res=1; a=a%mod; while(b){if(b&1)res=(a*res)%mod; a=(a*a)%mod; b>>=1;} return res; }
LL getInv(LL a,LL mod){ return quickModPow(a,mod-2,mod); }
const double EPS = 1E-10;
const int MOD = 1E9+7;
const int N = 500;
const int dx[] = {-1,1,0,0,-1,-1,1,1};
const int dy[] = {0,0,-1,1,-1,1,-1,1};
using namespace std;

struct Edge {
    int to,next;
    Edge() {}
    Edge(int to,int next):to(to),next(next) {}
};
struct MCS{
    Edge edge[N*N];
    int head[N],tot;
    int n,m;
    bool vis[N];
    int id[N];//编号
    int label[N];//与多少标号点相邻
    int order[N];//完美消除序列
    vector<int> V[N];

    void init(int n,int m) {
        this->n=n;
        this->m=m;
        for(int i=1; i<=n; i++)
            V[i].clear();
        memset(head,-1,sizeof(head));
        memset(order,0,sizeof(order));
        memset(label,0,sizeof(label));
        memset(vis,0,sizeof(vis));
        memset(id,0,sizeof(id));
        tot=0;
    }

    void addEdge(int x,int y) {
        edge[tot].to=y;
        edge[tot].next=head[x];
        head[x]=tot++;
    }

    void mcs() {
        for(int i=1; i<=n; i++)
            V[0].push_back(i);
        int maxx=0;
        int now=0;
        for(int i=1; i<=n; i++) { //从前往后
            bool flag=false;
            while(!flag) {
                for(int j=V[maxx].size()-1; j>=0; j--) { //从后往前
                    if(vis[V[maxx][j]])
                        V[maxx].pop_back();
                    else {
                        flag=true;
                        now=V[maxx][j];
                        break;
                    }
                }
                if(!flag)
                    maxx--;
            }
            vis[now]=true;
            //逆序存放
            order[n-i+1]=now;
            id[now]=n-i+1;

            for(int j=head[now]; j!=-1; j=edge[j].next) {
                int to=edge[j].to;
                if(!vis[to]) {
                    label[to]++;
                    V[label[to]].push_back(to);
                    maxx=max(maxx,label[to]);
                }
            }
        }
    }
    int bucket[N];//桶
    int judge() { //判断是否是弦图
        memset(vis,0,sizeof(vis));
        memset(bucket,0,sizeof(bucket));
        for(int i=n; i>0; i--) {
            int cnt=0;

            int ret=1;
            for(int j=head[order[i]]; j!=-1; j=edge[j].next)
                if(id[edge[j].to]>i)
                    vis[bucket[++cnt]=edge[j].to]=1;

            for(int j=head[bucket[1]]; j!=-1; j=edge[j].next) {
                int to=edge[j].to;
                if(to!=bucket[1]&&vis[to]) {
                    if(vis[to]) {
                        ret++;
                        vis[to]++;
                    }
                }
            }
            for(int j=1; j<=cnt; j++)
                vis[bucket[j]]=0;
            if(cnt&&ret!=cnt)
                return false;
        }
        return true;
    }
    int getMaximumClique() { //计算最大团、最小着色
        int res=0;
        for(int i=1; i<=n; i++)
            res=max(res,label[i]+1);
        return res;
    }
    int getMaximumIndependentSet() { //计算最大独立集、最小团覆盖
        memset(vis,0,sizeof(vis));
        int res=0;
        for(int i=1; i<=n; i++) {
            if(!vis[order[i]]) {
                res++;
                vis[order[i]]=true;
                for(int j=head[order[i]]; j!=-1; j=edge[j].next)
                    vis[edge[j].to]=true;
            }
        }
        return res;
    }
}mcs;
int main() {
    int n,m;
    scanf("%d%d",&n,&m);
    mcs.init(n,m);
    for(int i=1; i<=m; i++) {
        int x,y;
        scanf("%d%d",&x,&y);
        mcs.addEdge(x,y);
        mcs.addEdge(y,x);
    }
    mcs.mcs();

    int res=mcs.getMaximumIndependentSet();//最大独立集、最小团覆盖
    printf("%d\n",res);

    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值