题目链接:http://poj.org/problem?id=2942

题意:n个骑士要举行圆桌会议,但是有些骑士相互仇视,必须满足以下两个条件才能举行:

(1)任何两个互相仇视的骑士不能相邻,每个骑士有两个相邻的骑士(即如果只有一个骑士,则不能举行会议)

(2)圆桌会议坐下的骑士数量必须为奇数个

有一张名单列出m个相互仇视的骑士,如果遵守以上两个规则,可能是某些骑士不可能被安排坐下,一种情况是一个骑士仇视所有其他的骑士。如果一个骑士不可能被安排坐下,则将他从骑士名单中剔除,问有多少个骑士会被剔除掉。

1<=n<=100,1<=m<=1000000

分析:题目上好像是说所有不被剔除的骑士都要参加圆桌会议,我严重怀疑这道题的题意是不是不明确,要不然就是poj的数据有问题。所以我将这题的题意理解为:不一定要所有不被剔除的骑士都要参加圆桌会议,只需其中的一部分就行了,这样有些数据就能解释为什么了。

将骑士看成顶点,不互相仇视的骑士连边,建无向图,即建反图。构造无向图之后,先按要求(1),将所有能坐在一起的骑士分为一组,全部骑士分为若干组,每一组在图中是一个双连通分量。注意,这里我们要求的是点双连通分量,是点双,不是边双。为什么呢?因为我们是要剔除掉骑士,而骑士就是一个顶点了,并不是剔除掉仇恨关系,所以是点双。

每一个双连通分量就是一个环了,但是这只是找到了环,而题目要求的是顶点数为奇数的环,即奇圈。

那么怎么判断奇圈呢?这里有两个定理:

(1)如果一个双连通分量中存在一个奇圈,那么该双连通分量内的所有顶点都处在某个奇圈内。

在一个双连通分量中,必定存在一个圈经过该连通分量的所有节点,如果这个圈是奇圈,则该连通分量内所有的点都满足条件;若这个圈是偶圈,如果包含奇圈,则必定有另一个奇圈经过由剩下的点或该奇圈内至少2个点及其边构成的环。

(2)一个双连通分量含有奇圈当且仅当它不是一个二分图。

直观的想,对于一个二分图,从一个点出发要回到一个点显然要经过偶数个节点,所以肯定不存在奇圈。

所以判断一个双连通分量是否含有奇圈,只需判断该双连通分量是否是二分图就行了,而判二分图可以用交叉染色法。

交叉染色法就是在DFS过程中反复交换着用两种不同的颜色对未染色过的点染色,若某次DFS中当前点的子节点和当前节点同色,则找到奇圈。

想象一下二分图就像是河的两岸有两排节点,没染色一次就过河一次,那么相同颜色的节点必定在同一侧。一旦出现异侧有相同颜色的节点,就说明该图不是二分图了。

总结一下:首先求出图的补图,然后把点双连通分量找出,对于每个双连通分量判断是否为二分图,如果不是则将分量重的所有点标记,统计一下标记过的顶点个数ans,最后结果就是n-ans。

AC代码:

 #include<cstdio>
#include<cstring>
const int N=+;
struct EDGE{
int v,next;
}edge[N*N*];
int g,cnt,top,count,n,m;
int first[N],low[N],dfn[N],sta[N*N*],sm[N],map[N][N],color[N],part[N],mark[N];
int min(int a,int b)
{
return a<b?a:b;
}
void AddEdge(int u,int v) //建边
{
edge[g].v=v;
edge[g].next=first[u];
first[u]=g++;
}
int dfscol(int u,int col) //交叉染色法
{
int i,v;
color[u]=col;
for(i=first[u];i!=-;i=edge[i].next)
{
v=edge[i].v;
if(!part[v])
continue;
if(color[v]==col)
return ;
if(color[v]==&&dfscol(v,-col))
return ;
}
return ;
}
void color_solve() //二分判定
{
int i;
memset(part,,sizeof(part));
for(i=;i<count;i++)
part[sm[i]]=;
memset(color,,sizeof(color));
if(dfscol(sm[],)) //若含有奇圈
{
for(i=;i<count;i++)
mark[sm[i]]=;
}
}
void Tarjan(int u,int fa) //求双连通分量
{
int i,v;
low[u]=dfn[u]=++cnt;
sta[top++]=u;
for(i=first[u];i!=-;i=edge[i].next)
{
v=edge[i].v;
if(i==(fa^))
continue;
if(!dfn[v])
{
Tarjan(v,i);
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u])
{
count=; //将双连通分量记录起来。。刚开始这部分写错了,wa到死
sm[count++]=u;
sta[top]=-;
while(sta[top]!=v) //注意割点属于多个双连通分量,所以要弹到v,u不能弹出去
{
sm[count++]=sta[--top];
}
color_solve(); //判断该双连通分量是否为二分图
}
}
else
low[u]=min(low[u],dfn[v]);
}
}
void solve()
{
int i,j,u,v;
g=cnt=top=; //初始化
memset(low,,sizeof(low));
memset(dfn,,sizeof(dfn));
memset(first,-,sizeof(first));
memset(map,,sizeof(map));
memset(mark,,sizeof(mark)); for(i=;i<m;i++)
{
scanf("%d%d",&u,&v);
map[u][v]=map[v][u]=;
}
for(i=;i<=n;i++) //建反图
for(j=i+;j<=n;j++)
{
if(!map[i][j])
{
AddEdge(i,j);
AddEdge(j,i);
}
}
for(i=;i<=n;i++) //求双连通分量
if(!dfn[i])
Tarjan(i,-); int ans=;
for(i=;i<=n;i++) //统计已标记的顶点数
if(mark[i])
ans++;
printf("%d\n",n-ans);
}
int main()
{
while(scanf("%d%d",&n,&m))
{
if(n==&&m==)
break;
solve();
}
return ;
}

poj 2942 Knights of the Round Table(点双连通分量+二分图判定)的更多相关文章

  1. POJ 2942 Knights of the Round Table(双连通分量)

    http://poj.org/problem?id=2942 题意 :n个骑士举行圆桌会议,每次会议应至少3个骑士参加,且相互憎恨的骑士不能坐在圆桌旁的相邻位置.如果意见发生分歧,则需要举手表决,因此 ...

  2. POJ2942 Knights of the Round Table 点双连通分量 二分图判定

    题目大意 有N个骑士,给出某些骑士之间的仇恨关系,每次开会时会选一些骑士开,骑士们会围坐在一个圆桌旁.一次会议能够顺利举行,要满足两个条件:1.任意相互憎恨的两个骑士不能相邻.2.开会人数为大于2的奇 ...

  3. 【POJ】2942 Knights of the Round Table(双连通分量)

    http://poj.org/problem?id=2942 各种逗.... 翻译白书上有:看了白书和网上的标程,学习了..orz. 双连通分量就是先找出割点,然后用个栈在找出割点前维护子树,最后如果 ...

  4. POJ2942 Knights of the Round Table[点双连通分量|二分图染色|补图]

    Knights of the Round Table Time Limit: 7000MS   Memory Limit: 65536K Total Submissions: 12439   Acce ...

  5. POJ 2942.Knights of the Round Table (双连通)

    简要题解: 意在判断哪些点在一个图的  奇环的双连通分量内. tarjan求出所有的点双连通分量,再用二分图染色判断每个双连通分量是否形成了奇环,记录哪些点出现在内奇环内 输出没有在奇环内的点的数目 ...

  6. POJ - 2942 Knights of the Round Table (点双联通分量+二分图判定)

    题意:有N个人要参加会议,围圈而坐,需要举手表决,所以每次会议都必须是奇数个人参加.有M对人互相讨厌,他们的座位不能相邻.问有多少人任意一场会议都不能出席. 分析:给出的M条关系是讨厌,将每个人视作点 ...

  7. UVALive-3523 Knights of the Round Table (双连通分量+二分图匹配)

    题目大意:有n个骑士要在圆桌上开会,但是相互憎恶的两个骑士不能相邻,现在已知骑士们之间的憎恶关系,问有几个骑士一定不能参加会议.参会骑士至少有3个且有奇数个. 题目分析:在可以相邻的骑士之间连一条无向 ...

  8. POJ 2942 Knights of the Round Table 黑白着色+点双连通分量

    题目来源:POJ 2942 Knights of the Round Table 题意:统计多个个骑士不能參加随意一场会议 每场会议必须至少三个人 排成一个圈 而且相邻的人不能有矛盾 题目给出若干个条 ...

  9. poj 2942 Knights of the Round Table - Tarjan

    Being a knight is a very attractive career: searching for the Holy Grail, saving damsels in distress ...

随机推荐

  1. 解决 spring-test 出现 Failed to load ApplicationContext 的异常

    在使用spring-test的时候,在启动@Test的方法时,spring-test会去加载spring的配置文件,这个时候如果配置文件没有在 @ContextConfiguration 中写全,就会 ...

  2. loadrunner使用过程中的问题记录

    一.录制时选错应用类型,导致提示“loadrunner sockets proxy auto-starter mercury interactive corp.(2002)” 解决办法:重新选择正确的 ...

  3. 树莓派3b无驱动打印

    Linux系统下很少有对打印机做驱动支持,自己做起来又有非常麻烦,还好大多数打印机都能够支持escpos协议,因此我们可以做到无驱动打印. 1.安装python-usb库 git clone http ...

  4. 用C链表实现约瑟夫环问题

    问题:设有n个人围成一个圆圈,现从第s个人开始报数,数到第m的人出列,然后从出列的下一个人重新开始报数,数到第m的人再次出列,如此反复,直到所有的人全部出列为止.对于任意给定的n.s.m,求按出列次序 ...

  5. 利用VS2015开发python版本的caffe应用

    打开VS2015,选择“新建项目”->“其它语言”->“python”,VS会提示你安装PTVS(Python Tools for Visual Studio)插件,安装完毕后即可开始py ...

  6. X509证书申请以及PKCS#10 详解

    一.证书颁发 1.单证书的签发 1) 用户填写信息注册(或者由RA的业务操作员注册用户). 2) 用户信息传递到RA. 3) RA审核通过. 4) 用户请求发证. 5) RA审核通过. 6) 用户签发 ...

  7. ThreadPoolExecutor 使用说明

    它是一个ExecutorService,使用线程池中的线程执行提交的任务.通常我们使用Executors框架,定义使用. 线程池主要用来解决两类问题:通过缓存一定数量的可用线程,避免频繁的线程创建,销 ...

  8. 解决java读取大文件内存溢出问题

    1. 传统方式:在内存中读取文件内容 读取文件行的标准方式是在内存中读取,Guava 和Apache Commons IO都提供了如下所示快速读取文件行的方法: Files.readLines(new ...

  9. git解决代码提交冲突

    树冲突文件名修改造成的冲突,称为树冲突.比如,A同事把文件改名为A.C,B同事把同一个文件改名为B.C,那么B同事将这两个commit合并时,会产生冲突.如果最终确定用B同事的文件名,那么解决办法如下 ...

  10. python初学者随笔Week2

    一.集合 去重,把一个列表变成集合,自动去重 关系测试,测试两组数据的交集,并集,差集关系 集合是无序的 集合的操作: list_1 = [1,4,5,7,3,6,7,9] list_1 = set( ...