题目大意:给定n个骑士以及m个讨厌关系,让你找到满足以下关系的需要减去骑士的最少数:
1、每个骑士的两边必须是自己的朋友!
2、每个桌子上必须坐奇数个人,这就意味着必须转化为一个奇圈,如果一个骑士不能和其它骑士组成一个奇圈则除去,并且一个人则也除去,因为一个人不能坐一个桌子!
解题思路:
首先需要知道几个定理:
如果一个强连通分量上存在一个奇圈,则这个连通分量上的任意一个点都在奇圈中!
一个图是二分图当且仅当该图中不存在奇圈!
所以这道题需要先找出强连通分量,然后在强连通分量中找看是否存在奇圈,总人数减去可以组成奇圈的骑士的人数就是需要减去的骑士的人数!
首先对于强连通分量的求法:首先求出割点,首先求出割点: 给每个顶点定义一个low值, low(u)表示从u出发, 经过一条其后代组成的路
径和后向边, 所能到达的最小深度的顶点的编号(如果这个编号大于等于u的编号, 就说明它的后代无法到达比u深度更浅的顶点, 即无法到达u的祖先, 那么u就是个关节点, 具体做法是在DFS每次回溯后拿刚才扩展节点的low与当前节点的dfn比较, 若low>=dfn则当前节点是割点).
low(u)=min{dfn(u), min{low(w)|w是u的儿子}, min{dfn(w)|(u,w) 是一条后向边}}dfn(u)是深搜过程中对顶点的编号值.
在求割点的同时就可以通过一个全局的栈求出点的双连通分量, 具体做法是在DFS每次由当前顶点u深入下一顶点v时, 就将边uv进栈, 若在函数返回后判断出v是割点, 则出栈, 直到uv出栈, 刚才出栈的这些边就属于同一个点双连通分量.
对于点的强连通分量可以通过判断其是否是二分图来决定其是否存在奇圈,如果一个图中有偶数个点,存在一个奇圈,那么这个图的剩余的点就一定也可以组成一个奇圈!
然后就是根据这些题做的啊!
几乎是看别人的代码然后写的结果还是WA了一晚上啊!后来改对了但是还是不了解,看来以后要多多留意这方面的东西了,提供一个大牛的解析地址啊:http://www.answeror.com/?s=2942
下面是代码哈!
#include<iostream>
#include<cstdio>
#include<string>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<climits>
using namespace std;
#define max(a,b) (a)>(b)?(a):(b)
#define min(a,b) (a)<(b)?(a):(b)
#define fab(a) (a)>0?(a):0-(a)
#define arc(a) (a)*(a)
#define inf 10000000 //最大值的
#define exp 0.0000001 //浮点型的
#define N 1100 //记录开的数组
bool vis[N],sign[N];
int flag[N];
int map[N][N];
int pre[N];
typedef struct fun
{
int x,pre;
}rr;
fun a[N*N*2];
int Stack[N];
int sum,tot,len;
int dfn[N],low[N];
int n,m;
int p[N];
void add(int i, int j)
{
a[m].x=j;
a[m].pre=pre[i];
pre[i]=m++;
}
int find(int i,int s)
{
int j;
p[i]=s;
for(j=pre[i]; j!=0; j=a[j].pre)
{
int y=a[j].x;
if(vis[y]==false)//不在这次找奇圈的范围内
continue;
if(p[y]==p[i])//找到奇圈的
return 1;
if(p[y]==0)
{
if(s==1)
find(y,2);
else
find(y,1);
}
}
return 0;
}
void judge(int t)
{
int i;
memset(vis,0,sizeof(vis));
for(i=0; i<t; i++)
vis[flag[i]]=true;
for(i=0; i<t; i++)
{
memset(p,0,sizeof(p));
if(find(flag[i],1))
break;
}
if(i<t)
{
for(i=0; i<t; i++)
if(sign[flag[i]]==false)
{
sign[flag[i]]=true;
sum++;
}
}
return ;
}
void dfs(int x, int y)
{
int j;
dfn[x]=low[x]=tot++;//
Stack[len++]=x;
int i;
for(i=pre[x];i!=0; i=a[i].pre)
{
int s=a[i].x;//代表边的另一个节点的
if(s==y)//不能对一个点进行来回的
continue;
if(!dfn[s])//没有在栈中
{
dfs(s,x);
low[x]=min(low[x],low[s]);
if(low[s]>=dfn[x])//就是代表x是割点
{
flag[0]=x;
int t=1;
// for(j=1; Stack[len]!=s;flag[j++]=Stack[--len]);
/* for(j=len-1; Stack[j]!=x; j--)
{
flag[t++]=Stack[j];
len--;
}*/
for(j=len; Stack[j]!=s; )
{
flag[t++]=Stack[--j];
len--;
}
judge(t);//对flag前面的t个元素进行判断是否为奇圈的
}
}
else
low[x]=min(low[x],low[s]);
}
}
int main()
{
while(scanf("%d%d",&n,&m))
{
if(n==0 && m==0)
break;
memset(sign,0,sizeof(sign));
memset(pre,0,sizeof(pre));
memset(dfn,0,sizeof(dfn));
int i,j;
for(i=1; i<=n; i++)
for(j=1; j<=n; j++)
map[i][j]=1;
int s,t;
while(m--)
{
scanf("%d%d",&s,&t);
map[s][t]=map[t][s]=0;
}
m=0;
for(i=1; i<=n; i++)
for(j=1+i; j<=n; j++)
if(map[i][j])
{
add(i,j);
add(j,i);
}
sum=0;
tot=1;//代表的是对没个点的计数的
len=1;//代表的是栈的长度的
for(i=1; i<=n; i++)
if(!dfn[i])//没有在栈中
dfs(i,-1);//对其进行神搜处理的
printf("%d\n",n-sum);
}
return 0;
}