神奇的国度(HYSBZ-1006)

探讨了在K国竞赛中,如何根据参赛者之间的相互认识关系,将参赛者划分为最少数量的队伍,以避免作弊的问题。通过构建图论模型,利用弦图特性和MCS算法求解最小着色数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Problem Description

K国是一个热衷三角形的国度,连人的交往也只喜欢三角原则.他们认为三角关系:即AB相互认识,BC相互认识,CA
相互认识,是简洁高效的.为了巩固三角关系,K国禁止四边关系,五边关系等等的存在.所谓N边关系,是指N个人 A1A2
...An之间仅存在N对认识关系:(A1A2)(A2A3)...(AnA1),而没有其它认识关系.比如四边关系指ABCD四个人 AB,BC,C
D,DA相互认识,而AC,BD不认识.全民比赛时,为了防止做弊,规定任意一对相互认识的人不得在一队,国王相知道,
最少可以分多少支队。

Input

第一行两个整数N,M。1<=N<=10000,1<=M<=1000000.表示有N个人,M对认识关系. 接下来M行每行输入一对朋

Output

输出一个整数,最少可以分多少队

Sample Input

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

Sample Output

3

思路:将 n 个人 m 个关系视为 n 个点 m 条边,从而构成一个图,根据关系的描述可知,构成的图是一个弦图,而题目本质上就是要求最小着色数,对于弦图来说,直接跑 MCS 后求取最大的 label[i]+1 即可

Source Program

#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 = 1000000;
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<<1];
    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.getMaximumClique();//最大团、最小着色
    printf("%d\n",res);

    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值