poj2942 Knights of the Round Table

本文介绍了一道算法题的解决思路,该题需要找出点双联通分量并对其进行二分图染色,以判断是否存在奇圈。文章详细解释了通过DFS遍历来寻找点双联通分量的方法,并对每个分量进行染色以确定是否满足题目条件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Knights of the Round Table

题目背景:

poj2942

分析:这个题,每个知识点都不难,但是放到一起还是挺恶心的。首先我们明确我们要找的是点双联通分量,然后针对每一组点双联通分量进行二分图染色,一次来判定其是否为奇圈(满足题目要求),之后用总数减去奇圈中的点个数即可。

Source:

 

#include 
     
      
#include 
      
       
#include 
       
        
#include 
        
         
#include 
         
          
#include 
          
            #include 
           
             #include 
            
              using namespace std; inline void R(int &v) { char c = 0; bool p = true; v = 0; while(!isdigit(c)) { if(c == '-') p = false; c = getchar(); } while(isdigit(c)) { v = (v << 3) + (v << 1) + (c ^ '0'); c = getchar(); } if(!p) v = -v; } const int MAXN = 600 + 10; const int MAXM = 1000000 + 100; stack 
             
               s; int first[MAXN << 1], low[MAXN << 1], num[MAXN << 1], scc[MAXN << 1], scnt[MAXN << 1]; int n, m, x, y, z, cnt, tot, ind, color[MAXN << 1], in[MAXN << 1], temp[MAXN << 1]; bool exist[MAXN << 1], map[2000][2000], odd[MAXN << 1]; struct node { int next, to; } edge[MAXM << 1]; inline void create(int x, int y) { tot++; edge[tot].next = first[x]; first[x] = tot; edge[tot].to = y; } void pre() { /*多组数据,所以注意初始化*/ tot = ind = 0; memset(num, 0, sizeof(num)); memset(map, 0, sizeof(map)); memset(exist, 0, sizeof(exist)); memset(odd, 0 ,sizeof(odd)); memset(first, 0, sizeof(first)); } void read() { for(int i = 1; i <= m; ++i) R(x), R(y), map[x][y] = map[y][x] = true; for(int i = 1; i <= n; ++i) for(int j = 1; j <= n; ++j) if(i != j && !map[i][j]) create(i, j); } bool dfs1(int cur, int c) { /*双联通判奇环*/ /*直接用简单的二分图染色即可*/ color[cur] = c; for(int p = first[cur]; p; p = edge[p].next) { if(!in[edge[p].to]) continue; if(color[edge[p].to] == c) return true; else if(color[edge[p].to]) continue; else if(dfs1(edge[p].to, 3 - c)) return true; } return false; } void dfs(int cur, int fa) { num[cur] = low[cur] = ++ind; exist[cur] = true; s.push(cur); for(int p = first[cur]; p; p = edge[p].next) { if(!num[edge[p].to]) { int top = 0; dfs(edge[p].to, cur), low[cur] = min(low[edge[p].to], low[cur]); if(low[edge[p].to] >= num[cur]) { /*点双联的另一种求法,找到一个子节点满足上述*/ memset(in, false, sizeof(in)); int o = s.top(); while(o != edge[p].to) { exist[o] = false, s.pop(), in[o] = true; temp[++top] = o, o = s.top(); } /*当前栈中的在自己之上的所有点,包括自己都是一个双联通* /*出栈的时候自己不出栈,因为割点可能存在于多个双联通*/ exist[o] = false, s.pop(), in[o] = true; temp[++top] = o, in[cur] = true; memset(color, 0, sizeof(color)); if(dfs1(cur, 1)) { odd[cur] = true; while(top) odd[temp[top--]] = true; } } } else if(edge[p].to != fa && exist[edge[p].to]) low[cur] = min(low[cur], num[edge[p].to]); } } void work() { int ans = 0; for(int i = 1; i <= n; ++i) if(!num[i]) dfs(i, -1); for(int i = 1; i <= n; ++i) if(!odd[i]) ans++; cout << ans << '\n'; } int main() { while(scanf("%d%d", &n, &m) != EOF) { if(n == 0 && m == 0) break; pre(); read(); work(); } return 0; } 
             
            
           
          
         
        
       
      
     

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值