题目链接
http://poj.org/problem?id=2942
题目大意
有n个骑士要参加一个会议,其中有m对骑士互相憎恶,互相憎恶的骑士不能同时出席会议,n个骑士要坐在多个圆桌上,而且每桌至少要有3个骑士,每桌的骑士个数也必须是奇数个,问有多少骑士无论如何也不能出席会议。
思路
Tarjan算法求点双联通分支+DFS交叉染色法找奇环。
首先我们逆向思考此题的反问题:有多少骑士可能出席这个会议。我们对任意一对不互相憎恶的骑士连无向边,则一个骑士能出席会议当且仅当它处在某个简单奇环中,原问题转换成求有多少个点不在奇圈上。
那么,我们可以在这个图中求出它的所有点双联通分量,于是我们只需要在点双里找奇环了,点双上的奇环上的点都可以出席会议。
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXE 2001000
#define MAXV 2000
using namespace std;
struct edge
{
int u,v,next;
}edges[MAXE];
int head[MAXV],nCount=1;
void AddEdge(int U,int V)
{
edges[++nCount].u=U;
edges[nCount].v=V;
edges[nCount].next=head[U];
head[U]=nCount;
}
int n,m;
int stack[MAXE],top=0,dfn[MAXV],low[MAXV],dfs_cnt=0;
bool inStack[MAXV],vis[MAXE];
int belong[MAXV];
bool inbcc[MAXV],flag[MAXV]; //flag[i]=true表示点i可以参加会议
int color[MAXV];
bool DFS(int u)
{
for(int p=head[u];p!=-1;p=edges[p].next)
{
int v=edges[p].v;
if(inbcc[v]) //点v在点双里
{
if(color[v]==-1) //v还没被访问过
{
color[v]=(color[u]+1)%2;
if(DFS(v)) return true;
}
else if(color[v]==color[u]) return true; //v已经访问过并且和u颜色相同,则找到了奇环
}
}
return false;
}
void check(int u) //以u为起点检查奇环
{
memset(color,-1,sizeof(color));
color[u]=0;
if(DFS(u))
{
for(int i=0;i<=n;i++)
if(inbcc[i])
flag[i]=true;
}
}
void Tarjan_BCC(int u,int fa)
{
low[u]=dfn[u]=++dfs_cnt;
for(int p=head[u];p!=-1;p=edges[p].next)
{
int v=edges[p].v;
if(!dfn[v])
{
stack[++top]=p;
Tarjan_BCC(v,u);
low[u]=min(low[u],low[v]);
if(dfn[u]<=low[v])
{
memset(inbcc,false,sizeof(inbcc));
int size=0; //该点双大小
while(1)
{
size++;
int tmp=stack[top--];
inbcc[edges[tmp].u]=inbcc[edges[tmp].v]=true;
if(edges[tmp].u==u&&edges[tmp].v==v) break;
}
if(size>=3) check(u); //若该点双大小>=3,则DFS对该点双进行黑白染色
}
}
else if(dfn[v]<dfn[u]&&v!=fa) //该边为回边
{
stack[++top]=p;
low[u]=min(low[u],dfn[v]);
}
}
}
int map[MAXV][MAXV];
int main()
{
while(scanf("%d%d",&n,&m)!=EOF&&!(!n&&!m))
{
nCount=0;
dfs_cnt=0;
top=0;
memset(map,0,sizeof(map));
memset(head,-1,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(flag,false,sizeof(flag));
for(int i=1;i<=m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
map[a][b]=map[b][a]=-1;
}
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(map[i][j]==0) //i与j不互相仇恨
{
AddEdge(i,j);
AddEdge(j,i);
}
for(int i=1;i<=n;i++)
if(!dfn[i])
Tarjan_BCC(i,-1);
int sum=0;
for(int i=1;i<=n;i++)
sum+=flag[i];
printf("%d\n",n-sum);
}
return 0;
}