某个图的点双联通分支里面必然是没有割点存在的, 在搜索树中不停地找,如果找到割点
,我们便将栈中的所有边都找出来,然后放进一个点双连通块中即可。割点可以属于多个双连通块,
但是其他点和边只能属于一个块.
原题就是 亚瑟王要骑士开会, 给一些边,边链接两个骑士,这两个骑士不能坐一起,问最少要减去多少骑士
建立补图,表示边连接的两个骑士可以坐在一起。然后我们要找点连通块中的奇数圈,定理写在代码中了。如果某个骑士
不存在奇数圈中,那么他就不能参加会议。
关于二分图判断问题,用染色法 dfs一下就好了
#include <cstdio>
#include <cmath>
#include <cstring>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <set>
#include <vector>
#include <sstream>
#include <queue>
#include <typeinfo>
#include <fstream>
#include <map>
#include <stack>
typedef long long ll;
using namespace std;
const int MAXN = 1010;//点数
const int MAXM = 2000005;//边数,因为是无向图,所以这个值要*2
struct Edge
{
int to,next;
}edge[MAXM];
int head[MAXN],tot;
int Low[MAXN],DFN[MAXN],Stack[MAXN],Belong[MAXN];
int Index,top;
bool Instack[MAXN];
int block; //点双连通块数量
//int cut[MAXN]// 记录割点
bool ok[MAXN];
bool can[MAXN]; //标记
int tmp[MAXN]; //存点双连通分量中的点
int cc; //记录 这个连通块中有多少个点
int color[MAXN]; //二分图染色
void addedge(int u,int v)
{
edge[tot].to = v;edge[tot].next = head[u];
head[u] = tot++;
}
bool dfs(int u,int col){ //染色法判二分图
color[u]=col;
for(int i=head[u];i!=-1 ; i=edge[i].next){
int v=edge[i].to;
if( !ok[v]) continue; //如果不在这个联通块中,可以跳过
if(color[v]!= -1){ //若v染过色 ,(初值设为-1)
if(color[v]==col) //并且和相邻点颜色相同
return false;
continue; //否则继续
}
if(!dfs(v,!col)) //!-1 =0 ;
return false;
}
return true;
}
void Tarjan(int u,int pre)
{
int v;
Low[u] = DFN[u] = ++Index;
Stack[top++] = u;
Instack[u] = true;
// int son=0; // 统计某个点在搜索树中的儿子个数
for(int i = head[u];i != -1;i = edge[i].next)
{
v = edge[i].to;
if(v == pre) continue;
if( !DFN[v] )
{
Tarjan(v,u);
if( Low[u] > Low[v] ) Low[u] = Low[v];
//一条边 (u,v)是桥,当且仅当(u,v)为树枝边(u,v都在栈里面),且满足DFN(u)<Low(v)
//这个地方就是对点双连通快进行操作
//
//
if(u!=pre && Low[v] >= DFN[u] ) // 若找到 割点u ,把边一一取出直到遇见 (u,v)
{
block++;
int vn;
cc=0;
memset(ok,0,sizeof(ok));
do{
vn=Stack[--top];
Belong[vn]=block;
Instack[vn]=0;
ok[vn]=1; // 表示这个点是在连通块里面的
tmp[cc++]=vn;
}
while(vn!=v); // 取完
ok[u]=1;
memset(color,-1,sizeof(color)); //初值为-1
//将u点染色为0,判断能否形成二分图
if(!dfs(u,0)){ //定理1:若在该连通分量里面不可形成二分图 ,若为二分图则必有奇数圈,反之也成立
can[u]=1;
while(cc--) //定理 2 : 若一个双联通分中某些点在奇数圈中,则所有点也在某个奇数圈中。
can[tmp[cc]]=1;
}
}
}
else if( Instack[v] && Low[u] > DFN[v] ) //若
Low[u] = DFN[v];
}
}
void init()
{
tot = 0;
memset(head,-1,sizeof(head));
}
void solve(int n){
memset(DFN,0,sizeof(DFN));
memset(Instack,false,sizeof(Instack));
// memset(add_block,0,sizeof(add_block) );
// memset(cut,false,sizeof(cut) );
memset(can,0,sizeof(can));
Index=top=block=0;
for(int i=1;i<=n;i++)
if(!DFN[i])
Tarjan(i,0);
// printf("blo=%d\n",block);
int ans=n;
for(int i=1;i<=n;i++)
if(can[i])
ans--;
printf("%d\n",ans);
}
int arc[MAXN][MAXN];
int main(){
int n,m;
//freopen("1.txt","r",stdin);
while(~scanf("%d %d",&n,&m) &&(n||m) ){
init();
int u,v;
memset(arc,0,sizeof(arc));
for(int i=0;i<m;i++){
scanf("%d %d",&u,&v);
arc[u][v]=arc[v][u]=1;
}
for(int i=1;i<=n;i++)
for(int j=1;j<i;j++){
if(arc[i][j]==0){
// printf("%d %d\n",i,j);
addedge(i,j);
addedge(j,i);
}
}
solve(n);
}
return 0;
}