点双联通分量+二分图染色
1、首先根据题目给出的憎恨关系建图,然后求补图,这个图表示哪个骑士可以和哪个骑士坐在一起。
2、在图中求出圈,也就是双联通分量,这可以用到Tarjan算法的思想,通过Tarjan算法执行过程中退栈求出一个又一个双联通分量。
3、对于双联通分量我们需要在其中找到一个奇圈,这需要一个利用了DFS的交叉染色法,在代码中我建补图的时候没有建立自己和自己的边,当用这个方法找出来的奇圈这少有三个点在圈上。
4、对于在奇圈上的标记出来,最后计数一共有多少,就知道不能开会的有多少了
#include <iostream> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <stack> using namespace std; #define MAXN 1111 #define MAXE 2001000 struct Edge { int v, next; } e[MAXE]; int head[MAXE], cnt; int dfn[MAXN], low[MAXN],belong[MAXN]; bool insta[MAXN],gra[MAXN][MAXN];;//在不在栈中 int dep, blo; bool can[MAXN], ok[MAXN];//ok标记点书否属于当前环 int tem[MAXN], color[MAXN]; stack<int> sta; void init() { memset(head, -1, sizeof(head)); cnt = dep = blo = 0; memset(dfn, 0, sizeof(dfn)); while(!sta.empty()) sta.pop(); memset(insta, 0, sizeof(insta)); memset(gra, 0, sizeof(gra)); memset(can, 0, sizeof(can)); } void Addedge(int uu, int vv) { e[cnt].v = vv, e[cnt].next = head[uu]; head[uu] = cnt++; } //dfs染色 bool dfs( int u, int col) { color[u] = col; for(int i = head[u]; i != -1; i = e[i].next) { int v = e[i].v; if(!ok[v]) continue; if(color[v] != -1) { if(color[v] == col) return false; continue; } if(!dfs(v, !col)) return false; } return true; } void Tarjan(int u, int pre) { dfn[u] = low[u] = ++dep; sta.push(u); insta[u] = 1; //cout<<"u"<<" "<<u<<" "; for(int i = head[u]; i != -1; i = e[i].next) { int v = e[i].v; if(v == pre) continue; //cout<<"v "<<v<<" "; if(!dfn[v]) { Tarjan(v, u); low[u] = min(low[u], low[v]); if(low[v] >= dfn[u]) { int vn ; int cnt = 0; ++blo; memset(ok, 0, sizeof(ok)); do { vn = sta.top(); sta.pop(); insta[vn] = 0; ok[vn] = 1; tem[cnt++] = vn; belong[vn] = blo; }while(vn != v); ok[u] = 1; memset(color, -1, sizeof(color)); if(!dfs(u, 0)) { can[u] = 1; for( int k = 0; k < cnt; k++) can[tem[k]] = 1; } } } else if(insta[v]) low[u] = min(low[u], dfn[v]); } // cout<<endl; } void solve( int n) { for( int i = 1; i <= n; i++) if(!dfn[i]) Tarjan(i, -1); int ans = n; for( int i = 1; i <= n; i++) if(can[i]) ans --; printf("%d\n",ans); } int main() { int n, m, u, v; while(scanf("%d %d",&n, &m) != EOF &&(n + m) ) { init(); for( int i = 0; i < m; i++) { scanf("%d %d",&u, &v); gra[u][v] = gra[v][u] = 1; } for( int i = 1; i <= n; i++) for( int j = 1; j <= n; j++) if(i != j && gra[i][j] == 0) Addedge(i , j); solve(n); } return 0; }