【POJ2942】Knights of the Round Table-点双连通分量+判断奇环
测试地址:Knights of the Round Table
题目大意:有
做法:这一题需要用到点双连通分量+判断奇环。
题目给了我们骑士之间的仇恨关系,考虑到这样不好做,而数据范围不很大(
这里有一个结论:如果一个点双连通分量中存在奇环,那么整个点双连通分量中的点都可能处在奇环中。证明如下:假设一个点双连通分量中存在奇环,那么对于任意一个奇环外的点,向奇环中的任意两点分别连一条路径,因为它们同属一个点双连通分量,所以存在两条路径使它们不包含相同的点或边(除了选定的那个点),那么这两条路径就可以合为从奇环上一个点到另一个点的路径,而这两个点将奇环分为两条路径,一条有偶数条边,一条有奇数条边,那么只要根据新增路径边数的奇偶性选择删去哪一个部分,总能找到一个包含任意一个原奇环外的点的奇环,这样就证明了上面的结论。有了这个结论,我们就可以先用Tarjan算法求出图的点双连通分量,然后对于每个点双连通分量,判断这个点双连通分量有没有奇环即可。
怎么判断一个图有没有奇环呢?我们知道,二分图就是不存在奇环的图,那么我们只需对这个图进行2-染色,如果染完后有两个相邻的点颜色相同,那么这个图就存在奇环,反之就不存在。
最后还有一个问题,因为割点可以从属于多个点双连通分量,所以需要去重,去重的方法有很多,这里就不细说了。这样我们就解决了这一道题目。
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,m,tot,tim,first[1010],dfn[1010],low[1010],s[1010],p[1010],ans,rt;
int stack[1010]={0},top;
bool g[1010][1010];
struct edge {int v,next;} e[2000010];
bool vis[1010],vis2[1010],c[1010],flag;
void insert(int a,int b)
{
e[++tot].v=b;
e[tot].next=first[a];
first[a]=tot;
}
void dfs(int v)
{
vis[v]=1;
dfn[v]=++tim;
low[v]=dfn[v];
for(int i=first[v];i;i=e[i].next)
{
if (!vis[e[i].v])
{
dfs(e[i].v);
if (low[e[i].v]>=dfn[v]) c[v]=1;
low[v]=min(low[v],low[e[i].v]);
}
else low[v]=min(low[v],dfn[e[i].v]);
}
}
void tarjan()
{
memset(vis,0,sizeof(vis));
memset(c,0,sizeof(c));
tim=0;
for(int i=1;i<=n;i++)
if (!vis[i]) dfs(i);
}
int color(int v)
{
vis2[v]=1;
stack[++top]=v;
int siz=1;
for(int i=first[v];i;i=e[i].next)
{
if (!vis2[e[i].v])
{
s[e[i].v]=(s[v]+1)%2;
if (!c[v]||low[e[i].v]<dfn[v]) siz+=color(e[i].v);
}
else if (s[e[i].v]==s[v]) flag=1;
}
return siz;
}
void find(int v)
{
vis[v]=1;
if (c[v])
{
s[v]=0;
vis2[v]=1;
int cnt=0;
for(int i=first[v];i;i=e[i].next)
if (low[e[i].v]>=dfn[v])
{
flag=0;top=0;
s[e[i].v]=1;
int siz=color(e[i].v)+1;
if (flag&&siz>2)
{
cnt++;
ans+=siz;
p[v]++;
for(int i=1;i<=top;i++)
p[stack[i]]++;
}
}
ans-=max(p[v]-1,0);
}
for(int i=first[v];i;i=e[i].next)
if (!vis[e[i].v]) find(e[i].v);
}
void solve()
{
memset(vis,0,sizeof(vis));
memset(vis2,0,sizeof(vis2));
memset(p,0,sizeof(p));
memset(s,-1,sizeof(s));
ans=0;
for(int i=1;i<=n;i++)
if (!vis[i]) rt=i,find(i);
}
int main()
{
while(scanf("%d%d",&n,&m)&&n)
{
memset(first,0,sizeof(first));
memset(g,0,sizeof(g));
for(int i=1;i<=m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
g[a][b]=g[b][a]=1;
}
tot=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if (!g[i][j]&&i!=j) insert(i,j);
tarjan();
solve();
printf("%d\n",n-ans);
}
return 0;
}