[UVA1364 | POJ | NC]Knights of the Round Table | Tarjan 求点双 | 二分图 | 综合图论

简介: 我们可以很轻松地发现,被提出的都是在点双连通分量之外的,比如该图中的1 和 5 ,那么怎么判断哪些点不在环中呢?此时我们还可以逆向思考,不 在 环 中 的 = = 总 的 − 在 环 中 的,所以说现在问题就转换成了满足条件的环内的点的个数

评测地址:

网址1

网址2


题目描述:


3a54da2e730b445f937c0456def7301a.png

11ec0ea8c40848d3896ff72d252ef2c4.png


题意:


给出n位骑士,然后有m个关系,每个关系以格式:

a   b给出,表达骑士a 不能和b 相邻,问要想使得最多的奇数个骑士在一起围成一圈,要剔除多少人


solution:


一. part1 : 首先解决这道题要用到的知识点:


1. 建立补图

2. Tarjan求解点双连通分量(V-DCC)并记录  洛谷模板题

3. 奇环

4. 二分图的判定(二分图染色)


二. part2 概念解析:


  1. 所谓补图,就是将原图中连着的边去掉,而将原来没有连的边连接起来。加入原图为G,我们设其补图为G ′ ,那么说满足以下两个条件:

G ∩ G ′ = ∅

G ∪ G ′ = U

其中U 代表全集,即包含n 个点,( n − 1 ) ∗ n / 2 条边的图的集合


  1. Tarjan求解点双连通分量的过程和求解边双连通分量的过程稍有不同,可以参考博客,这里直接截图引入:


801dd23f83fc4e02a037a1037a8d46ed.png

85fcb52da4fc4d9289b01498d0291a80.png


奇环

一个环内,有奇数个点,那么这个环就是个奇环


二分图的判定

用二分图染(交叉)色的方法可以判断图是不是一个二分图


三. part3 思路解析


  1. 发现直接用给出的数据不好进行处理,因为给出的是不能相邻的关系,这里我们逆向思考,建立补图,然后图的意义就发生了翻天覆地的变化:

由原本相连的边是不能相邻的关系转换成了相连的点可以相邻


  1. 根据题中的要求,要使得相邻的两个人靠在一起,说明能够满足条件的骑士的度数必定≥ 2 \geq 2≥2,所以说对于那些点的度数≤ 1 \leq 1≤1的点,都是要被剔除的。

哪些点要被剔除呢?


4b8f155620d74f90b6c2bd71c35133d5.png

图片来源

我们可以很轻松地发现,被提出的都是在点双连通分量之外的,比如该图中的1 和 5 ,那么怎么判断哪些点不在环中呢?

此时我们还可以逆向思考,不 在 环 中 的 = = 总 的 − 在 环 中 的,所以说现在问题就转换成了满足条件的环内的点的个数


对于那些满足条件的点,留在了环中,但是他们一定满足条件吗?

答案是不一定,因为环中可能不是奇数个点,可能是偶数个

对于一个偶数的环,都是要被整体剔除的!


所以说现在问题就转换成了求一个点双强连通分量是不是有奇环:

给出一个结论:


结论1:

双连通分量含有奇圈,则必定不是一个二分图,反之亦然成立所以说如果对一个点双强连通分量染色不成功,那么说这个点双强连通分量中必然含有一个奇环

结论2:

如果一个V-DCC中有一个奇环,那么该V-DCC所有点都在某一个奇环上.为什么呢?假设有一个奇环上的两个点x , y ,该奇环外一个点zz,因为在同一个V-DCC上,x , y , z 肯定在同一个环上.路径x → z → y → x 中,因为x , y 在同一个奇环上,y → x 至少有两条路可以走,并且这两条路的奇偶性不同,因此不管怎样都可以找到一个包含z 的奇环x → z → y → x

(如果一个点双连通分量内的某些顶点在一个奇环中(即点双连通分量含有奇环),那么这个点双连通分量的其他顶点也在某个奇环中;)

所以说现在问题就转换成了如何求一个图是不是二分图,这里就用到了二分图交叉染色来处理!


统计答案

对于上面统计出来的可以加入的点,我们统一标记起来,比如 judge[i] 为1的时候表示这个点可以加入,而为0的时候,表示这个点不可以加入,所以说我们记录答案为 ans=n,遍历n个点,遇到 judge[i] == 1 的情况时,ans−−即可


至此讲解结束


ac_code:


int n,m,root,cntVDCC;
struct node{
  int u,v,nex;
}e[maxn << 1];
int dfn[maxn],low[maxn],cnt,head[maxn],dfc;
void add(int u,int v) {
  e[cnt].u = u;
  e[cnt].v = v;
  e[cnt].nex = head[u];
  head[u] = cnt ++;
}
typedef pair<int,int> PII;
map<PII,bool> mp;
bool exist[1007][1007];
stack<int> stk;
void ClearStack() {
  while(stk.size()) stk.pop();
}
int col[1007];
vector<int> VDCC[1007];
void _Init() {
  dfc = cntVDCC = cnt = 0;
  root = 0;
  mp.clear();
  for(int i=1;i<=n;i++) {
    head[i] = -1;
    dfn[i] = low[i] = 0;
    col[i] = 0;
    VDCC[i].clear();
  }
}
bool judge[1007],canVis[1007];
void Tarjan(int u){
  dfn[u] = low[u] = ++ dfc;
  if(head[u] == -1 && u == root) {
    ++ cntVDCC;
    VDCC[cntVDCC].push_back(u);
  }
  for(int i=head[u];~i;i=e[i].nex) {
    int to = e[i].v;
    if(!dfn[to]) {
      stk.push(to);
      Tarjan(to);
      low[u] = min(low[u],low[to]);
      if(low[to] >= dfn[u]) {
        cntVDCC ++;
        while(stk.top() != to) {
          VDCC[cntVDCC].push_back(stk.top());
          stk.pop();
        }
        VDCC[cntVDCC].push_back(stk.top());
        stk.pop();
        VDCC[cntVDCC].push_back(u);
        /**
        VDCC[cntVDCC].push_back(u);
        int tp;
        do{
          tp = stk.top();
          stk.pop();
          VDCC[cntVDCC].push_back(tp);
        }while(tp != to);
        **/
      }
    }///else if(to != fa) low[u] = min(low[u], dfn[to]);
    else low[u] = min(low[u], dfn[to]);
  }
}
bool dfs(int u){
  for(int i=head[u];~i;i=e[i].nex) {
    int to = e[i].v;
    if(canVis[to]) {
      if(!col[to]) {
        col[to] = 3 - col[u];
        if(!dfs(to)) return false;
      }else if(col[u] + col[to] != 3) return false;
    }
  }
  return true;
}
int main() {
  while(cin >> n >> m && (n + m)) {
    _Init();
    ClearStack();
    memset(exist,0,sizeof exist);
    for(int i=1;i<=m;i++) {
      int u = read,v = read;
//      mp[{u,v}] = mp[{v,u}] = 1;///TLE罪魁祸首
      exist[u][v] = exist[v][u] = 1;///标记边
    }
    for(int i=1;i<=n;i++){
      for(int j=1+i;j<=n;j++){
//        if(mp[{i,j}]) continue;
        if(exist[i][j]) continue;
        add(i,j);//建立补图
        add(j,i);
      }
    }
    for(int i=1;i<=n;i++){
      if(!dfn[i]) {
        root = i;
        Tarjan(i);
      }
    }
    memset(judge,0,sizeof judge);
    memset(canVis,0,sizeof canVis);
//    debug(cntVDCC);
    for(int i=1;i<=cntVDCC;i++) {
      for(int x:VDCC[i]) canVis[x]=1;///标记为可以通过
      if(!dfs(VDCC[i][0])) {///染色不成功,含有奇环
        for(int x:VDCC[i]) judge[x] = 1;
      }
      for(int x:VDCC[i]) canVis[x]=0,col[x]=0;///撤销标记以及颜色
    }
    int ans = n;//统计答案
    for(int i=1;i<=n;i++) {
      if(judge[i]) -- ans;
    }
    printf("%d\n",ans);
  }
  return 0;
}
目录
相关文章
|
6月前
|
算法
poj 2479 Maximum sum(求最大子段和的延伸)
看完最大连续子段和 的 dp算法 这个很容易理解,我用dplift[i]保存第1到第i个之间的最大子段和,dpright[i]保存第i到第n个之间的最大子段和,最终结果就是dplift[i]+dpright[i+1]中最大的一个。
28 0
|
6月前
|
存储
poj 3254 Corn Fields (状态压缩dp)
状态压缩dp其实就是用二进制来表示所有的状态,比如这题, 我们在某一行可以这样取0 1 0 1 1 0 1,用1代表取了,0代表没取,因为这点,它的数据量也限制在20以内,所有看到这样数据量的题目可以先考虑一下状态压缩dp。对于有多行的数据,所有状态的总数必然很庞大,而且不用特殊的方法想要存储这些状态是不太现实的。既然每个点只有这两种情况,我们可以用二进制的一位来表示,0 1 0 1 1 0 1就可以表示为二进制0101101也就是十进制的45,如果我们想要枚举6个点的所有状态,我们只需要从0到2^6取其二进制就可以了,并不会重复或是多余。
18 0
|
6月前
华为机试HJ91:走方格的方案数
华为机试HJ91:走方格的方案数
|
6月前
|
算法
华为机试HJ82:将真分数分解为埃及分数
华为机试HJ82:将真分数分解为埃及分数
|
6月前
|
存储 容器
华为机试HJ41:称砝码(深度优先遍历dfs-Depth First Search)
华为机试HJ41:称砝码(深度优先遍历dfs-Depth First Search)
|
6月前
|
大数据 测试技术 索引
华为机试HJ25:数据分类处理
华为机试HJ25:数据分类处理
|
6月前
华为机试HJ45:名字的漂亮度
华为机试HJ45:名字的漂亮度
|
6月前
|
人工智能 算法 测试技术
华为机试HJ52:计算字符串的距离(动态规划)
华为机试HJ52:计算字符串的距离(动态规划)
|
人工智能 算法 BI
LeetCode 周赛 341 场,模拟 / 树上差分 / Tarjan 离线 LCA / DFS
上周末有单双周赛,双周赛我们讲过了,单周赛那天早上有事没参加,后面做了虚拟竞赛,然后整个人就不好了。前 3 题非常简单,但第 4 题有点东西啊,差点就放弃了。最后,被折磨了一个下午和一个大夜总算把第 4 题做出来了,除了新学的 Tarjon 离线算法,这道题还涉及到树上差分、前缀和、DFS、图论等基础知识,几度被折磨得想要放弃。这种感觉,似乎和当年在 LeetCode 上做前 10 题的时候差不多哈哈。
66 0
【CCCC】L3-018 森森美图 (30分),计算几何+判断三点共线+bfs最短路
【CCCC】L3-018 森森美图 (30分),计算几何+判断三点共线+bfs最短路
113 0