UVA1364 Knights of the Round Table

41 篇文章 0 订阅
  • 题目描述: 一群骑士,要有若干个(三个及以上的奇数个)骑士围成一个圈开会,有m组冲突关系,对于每一组(i,j),表示骑士i和骑士j开会不能相邻,求有多少个骑士一场会议都不能参加。
  • 限制一:想象一下,一个圆桌是否可以看成一个简单环(一个双联通子图),假设现在有所有种的会议组合,对于一组冲突(i,j),相当于所有i,j相邻的圆桌会议组合都不合法了,即无向边(i,j)断开;因此第一个限制条件就有了处理办法:所有点构成一个团(点与点两两相连),将每组冲突对应的边从团中扣去
  • 现在考虑第二个限制条件:每个圆桌会议只能有奇数个骑士参加(奇环)。蓝书中有以下精彩证明:如果一个点·双联通分量是二分图,那么其一定不存在奇环,若不是二分图,则一定存在一个奇环。假设u1 u2为奇环中的两个点,则必然有u1->u2一奇一偶(针对点的数目说)的两条路径,也就是说不论路径u1->v->u2是奇是偶,总能构造出一个包含v的奇环。于是有:对于一个 点·双联通分量,若它不是二分图,则其中的每个点都在一个奇环中,都可以参加圆桌会议。
  • 在这里插入图片描述
  • 如何判断是二分图:根据点·双连通分量的连通性,用染色法即可,如果到达的一个改分量中的点的颜色与当前点冲突,则不是二分图,具体详见代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include<bits/stdc++.h>
#include <vector>
#include <cstring>
#include <fstream>
using namespace std;
int n,m,last[1005],cnt,bccnt,low[1005],dfn[1005],dindex,stop,u,v,bccno[1005],color[1005],num;
bool g[1005][1005],iscut[1005],bccbl[1005][1005],ans[1005];//bccbl[i][j]判断j是否在第i个bcc中
vector<int>bcc[1005];
struct edge{
    int u,v,next;
}e[200010];
edge sta[200010];
inline void add(int u,int v)
{
    e[++cnt].v=v;
    e[cnt].next=last[u];
    last[u]=cnt;
    e[++cnt].v=u;
    e[cnt].next=last[v];
    last[v]=cnt;
}
void dfs(int u,int fa)
{
    low[u]=dfn[u]=++dindex;
    int child=0;
    for(int i=last[u];i;i=e[i].next)
    {
        int v=e[i].v;
        if(!dfn[v])
        {
            child++;
            sta[++stop]={u,v,0};
            dfs(v,u);
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u])
            {
                iscut[u]=1;
                bccnt++;
                bcc[bccnt].clear();
                while(1)
                {
                    edge x=sta[stop--];
                    if(bccno[x.u]!=bccnt)
                    {
                        bccno[x.u]=bccnt;
                        bcc[bccnt].push_back(x.u);
                        bccbl[bccnt][x.u]=1;
                    }
                    if(bccno[x.v]!=bccnt)
                    {
                        bccno[x.v]=bccnt;
                        bcc[bccnt].push_back(x.v);
                        bccbl[bccnt][x.v]=1;
                    }
                    if(x.u==u&&x.v==v) break;
                }
            }
        }
        else if(v!=fa&&dfn[v]<dfn[u])
        {
            low[u]=min(low[u],dfn[v]);
            sta[++stop]={u,v,0};
        }
    }
    if(fa==-1&&child==1)
    iscut[u]=0;
}
void findbcc()
{
    memset(low,0,sizeof(low));
    memset(dfn,0,sizeof(dfn));
    memset(bccno,0,sizeof(bccno));
    memset(iscut,0,sizeof(iscut));
    memset(bccbl,0,sizeof(bccbl));
    dindex=bccnt=0;
    for(int i=1;i<=n;i++)
    if(!dfn[i])
    dfs(i,-1);
}
bool getcolor(int x,int bc)
{
    for(int i=last[x];i;i=e[i].next)
    {
        int v=e[i].v;
        if(!color[v]&&bccbl[bc][v])
        {
            color[v]=(color[x]==1?2:1);
            if(!getcolor(v,bc)) return false;
        }
        else if(color[v]==color[x]&&bccbl[bc][v])
            return false;
    }
    return true;
}
void intt()
{
    for(int i=1;i<=cnt;i++)
    e[i].v=e[i].next=0;
    memset(last,0,sizeof(last));
    memset(g,0,sizeof(g));
    memset(ans,0,sizeof(ans));
    num=cnt=0;
}
int main()
{
//    freopen("1.txt","w",stdout);
    while(scanf("%d%d",&n,&m)&&n+m!=0)
    {
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&u,&v);
            g[v][u]=g[u][v]=1;
        }
        for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
        if(!g[i][j])
        add(i,j);
        findbcc();
        for(int i=1;i<=bccnt;i++)
        {
            for(int j=0;j<bcc[i].size();j++)
            color[bcc[i][j]]=0;
            color[bcc[i][0]]=1;
            if(!getcolor(bcc[i][0],i))
            for(int j=0;j<bcc[i].size();j++)
            ans[bcc[i][j]]=1;
        }
        for(int i=1;i<=n;i++)
        if(!ans[i]) num++;
        cout<<num<<endl;
        intt();
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哈希表扁豆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值