关闭

圆桌骑士,蓝书P316LA3523(tarjan求双连通分量算法,二分图判断,奇圈)

411人阅读 评论(0) 收藏 举报
分类:

重点重点重点,每一个不在环内的边本身也是单独的一个双连通分量。双连通分量定义:若一个无向图中的去掉任意一个节点(一条边)都不会改变此图的连通性,即不存在割点(桥),则称作点(边)双连通图。刘汝佳白书P314也给出了这个等价定义,但是“任意两点之间至少存在两条点不重复的路径”这个定义明显不包含单独边这种情况。tarjan算法都是基于标准定义的。

本题目的经典程度不用多赘述。直接总结:
1.Edge e = (Edge){u, v};//此处语法细节需要学习 结构体 的初始化方式。
2.if(fa < 0 && child == 1) iscut[u] = 0;//当fa<0且child == 1的时候,就是一条边,对于一条边无割顶
3.bccno数组标记当前访问的连通分量的点。
4.用color数组来判断是否为二分图。

// LA3523 Knights of the Round Table
// Rujia Liu
#include<cstdio>
#include<stack>
#include<vector>
#include<algorithm>
#include<cstring>

using namespace std;

struct Edge { int u, v; };

const int maxn = 1000 + 10;
int pre[maxn], iscut[maxn], bccno[maxn], dfs_clock, bcc_cnt; // 割顶的bccno无意义
vector<int> G[maxn], bcc[maxn];

stack<Edge> S;

int dfs(int u, int fa) {
  int lowu = pre[u] = ++dfs_clock;
  int child = 0;
  for(int i = 0; i < G[u].size(); i++) {
    int v = G[u][i];
    Edge e = (Edge){u, v};//此处语法细节需要学习 
    if(!pre[v]) { // 没有访问过v
      S.push(e);
      child++;
      int lowv = dfs(v, u);
      lowu = min(lowu, lowv); // 用后代的low函数更新自己
      if(lowv >= pre[u]) {
        iscut[u] = true;
        bcc_cnt++; 
        for(;;) {
          Edge x = S.top(); S.pop();
          if(bccno[x.u] != bcc_cnt) { bcc[bcc_cnt].push_back(x.u); bccno[x.u] = bcc_cnt; }
          if(bccno[x.v] != bcc_cnt) { bcc[bcc_cnt].push_back(x.v); bccno[x.v] = bcc_cnt; }
          if(x.u == u && x.v == v) break;
        }
      }
    }
    else if(pre[v] < pre[u] && v != fa) {
      S.push(e);
      lowu = min(lowu, pre[v]); // 用反向边更新自己
    }
  }
  if(fa < 0 && child == 1) iscut[u] = 0;//当fa<0且child == 1的时候,就是一条边,对于一条边无割顶 
  return lowu;
}

void find_bcc(int n) {
  // 调用结束后S保证为空,所以不用清空
  memset(pre, 0, sizeof(pre));
  memset(iscut, 0, sizeof(iscut));
  memset(bccno, 0, sizeof(bccno));
  dfs_clock = bcc_cnt = 0;
  for(int i = 0; i < n; i++)
    if(!pre[i]) dfs(i, -1); 
}

int odd[maxn], color[maxn];
bool bipartite(int u, int b) {
  for(int i = 0; i < G[u].size(); i++) {
    int v = G[u][i]; if(bccno[v] != b) continue;
    if(color[v] == color[u]) return false;
    if(!color[v]) {
      color[v] = 3 - color[u];
      if(!bipartite(v, b)) return false;
    }
  }
  return true;
}

int A[maxn][maxn];

int main() {
  int kase = 0, n, m;
  while(scanf("%d%d", &n, &m) == 2 && n) {
    for(int i = 0; i < n; i++) G[i].clear();

    memset(A, 0, sizeof(A));
    for(int i = 0; i < m; i++) {
      int u, v;
      scanf("%d%d", &u, &v); u--; v--; A[u][v] = A[v][u] = 1;
    }
    for(int u = 0; u < n; u++)
      for(int v = u+1; v < n; v++)
        if(!A[u][v]) { G[u].push_back(v); G[v].push_back(u); }//!!!图的邻接存储!!! 

    find_bcc(n);

    memset(odd, 0, sizeof(odd));
    for(int i = 1; i <= bcc_cnt; i++) {
      memset(color, 0, sizeof(color));
      for(int j = 0; j < bcc[i].size(); j++) bccno[bcc[i][j]] = i; // 将所有节点暂时标记为当前要访问的双连通分量,主要是处理割顶
      int u = bcc[i][0];
      color[u] = 1;//二分图一定不是简单奇圈,简单奇圈一定不是二分图 ,刘巧妙用color变量来判断是否为二分图 
      if(!bipartite(u, i))
        for(int j = 0; j < bcc[i].size(); j++) odd[bcc[i][j]] = 1;
    }
    int ans = n;
    for(int i = 0; i < n; i++) if(odd[i]) ans--;
    printf("%d\n", ans);
  }
  return 0;
}
0
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

Tarjan三大算法之双连通分量(割点,桥)

Robert Endre Tarjan是一个美国计算机学家,他传奇的一生中发明了无数算法,统称为Tarjan算法。其中最著名的有三个,分别用来求解 1) 无向图的双连通分量 2) 有向图的强连通分...
  • fuyukai
  • fuyukai
  • 2016-04-23 11:25
  • 4881

Tarjan三大算法之双连通分量(双连通分量)

定义: 对于一个连通图,如果任意两点至少存在两条点不重复路径,则称这个图为点双连通的(简称双连通);如果任意两点至少存在两条边不重复路径,则称该图为边双连通的。点双连通图的定义等价于任意两条边都同在...
  • fuyukai
  • fuyukai
  • 2016-05-03 16:18
  • 9081

强连通算法--Tarjan个人理解+详解

首先我们引入定义: 1、有向图G中,以顶点v为起点的弧的数目称为v的出度,记做deg+(v);以顶点v为终点的弧的数目称为v的入度,记做deg-(v)。 2、如果在有向图G中,有一条有向道路,则v称为...
  • mengxiang000000
  • mengxiang000000
  • 2016-06-14 17:31
  • 4151

【poj2942】圆桌骑士Knights of the Round Table【双连通分量】【二分图】【奇圈】

传送门:http://poj.org/problem?id=2942 尽管我承认这题我几乎是对着书抄的代码(因为我还不熟- -),但是我还是WA了三次- -数组又没清零。 基本思想就是: 首先把...
  • ZMOIYNLP
  • ZMOIYNLP
  • 2015-03-03 16:54
  • 772

LA 3523 tarjian求双连通分量+二分判奇圈

白书上有详细解说 代码与书上有些不同,但核心是相通的 //LA 3523 点双连通+二分判奇圈 #include #include #include #include #include ...
  • driver13
  • driver13
  • 2016-01-25 14:53
  • 374

LA 3523(双连通分量,判断二分图)

以后这个代码用来当模板 #include #include #include #include #include using namespace std; const int maxn=...
  • mtxxxx
  • mtxxxx
  • 2016-11-21 23:12
  • 109

LA 3523 Knights of the Round Table(点双连通分量+二分图判断)

题意:有n个骑士经常举行圆桌会议,商讨大事,每次圆桌会议至少应有3个骑士参加,且互相憎恨的骑士不能坐在圆桌旁的相邻位置,如果发生意见分歧,则需要举手表决,因此参加会议的骑士数目必须是奇数,以防赞同和反...
  • u014258433
  • u014258433
  • 2016-07-24 15:37
  • 229

poj2942圆桌骑士【点双连通分量+二分图判断】

/*********** poj2942 2015.11.11-2015.11.13 不懂 暂时放下了 2015.11.18 4792K 1172MS G++ ***********/ #includ...
  • zhou_yujia
  • zhou_yujia
  • 2015-11-18 09:49
  • 546

LA 3523 Knights of the Round Table 边双连通分量+二分图判定

收获: 1、面对在图中分离出一个圈的问题时,要想到用无向图的边双连通分量 2、当强调这个圈满足边数的奇偶条件时,要想到和二分图有关 #pragma warning(disable:4786) #...
  • jijijix
  • jijijix
  • 2017-03-14 17:10
  • 143

[POJ]2942 圆桌骑士 点双连通分量 + 二分图染色

#include #include #include #include using namespace std; #define clear(a) memset(a,0,sizeof(a)) #def...
  • MaxMercer
  • MaxMercer
  • 2017-07-13 09:04
  • 157
    个人资料
    • 访问:11885次
    • 积分:1316
    • 等级:
    • 排名:千里之外
    • 原创:126篇
    • 转载:0篇
    • 译文:0篇
    • 评论:2条
    最新评论