题目大意

有N个骑士,给出某些骑士之间的仇恨关系,每次开会时会选一些骑士开,骑士们会围坐在一个圆桌旁。一次会议能够顺利举行,要满足两个条件:1.任意相互憎恨的两个骑士不能相邻。2.开会人数为大于2的奇数。若某个骑士任何会议都不能参加,那么就必须将他踢出,给出骑士之间的仇恨关系,问最少需要踢出多少个骑士?

总体思路

依题意,把骑士看作节点,每个骑士向其不仇视的骑士连一条无向边。如果一个骑士所代表的节点在一个奇环内,则该骑士便可以与环内节点所代表的骑士一起开会,所以我们要找到那个奇环。

我们要先找到环,再看看它是不是奇的。

利用的性质

  • 图中所有的环包含在图的点双连通分量中(因为环中不存在割点)(图的点双连通分量为图的极大不存在割点的子图)。
  • 如果一个双连通分量中存在奇环,则该双连通分量中的所有节点都属于一个奇环(如果存在一个奇环,则该双连通分量中的其它节点必由两条路径与奇环中的两个节点相连(否则路径上就有割点了)。两个节点之间因为在奇环中,所以连接两节点奇环内必有两条路径,一条是奇的,一条是偶的。因为经过那个其它节点与这两个节点的路径长度是固定的,所以命题成立)。

求点双连通分量:Tarjan ——的一种方法

在基本Tarjan算法的基础上构造一个维护节点的栈,其保证栈内节点cur和cur以上的所有节点都位于一个或多个双连通分量中。如果cur是割点,使得cur是某个遍历出的相邻节点v所属的双连通分量中DfsN值最小的节点,则一直将栈内v及以上的所有节点和cur纳入一个双连通分量中,将v及以上所有节点出栈。

注意事项

  • 永远记住如果cur是割点不代表所有与cur相邻的节点都属于不同的双连通分量!所以是出栈到v,而不是出栈到cur。
  • 即使cur是根节点且cnt<1(其实在此题中cnt根本不需要),也要进行出栈操作,因为此算法是一边遍历一边设双连通分量,如果根遍历到的第一个v节点便与根属于一个双连通分量,cnt不小于1导致了跳过。
  • 记住判断割点的依据是cur->DfsN <= v->Low,而不是cur->DfsN <= cur->Low。
  • 根节点可能不是割点,但是根节点必须在一个点双连通分量中,所以要注意Dfs完后的出栈操作。

判断奇环——二分图判定

利用染色的方法,将节点分为黑白两种颜色,一个节点要尝试将下一个节点染成与自己不同的颜色。如果下一个节点已有与自己相同的颜色,则此非二分图,存在奇环。如果最终得到了一个二分图,则不存在奇环。

AC待优化代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;#define LOOP(i,n) for(int i=1; i<=n; i++)
const int MAX_NODE = 5000, MAX_EDGE = 1000010 * 2;struct Node;
struct Edge;struct Node {int Id, DfsN, Low, Color;bool InBlock, InTable;//inTable:能加入圆桌会议Edge *Head;
}_nodes[MAX_NODE];struct Edge {Node *From, *To;Edge *Next, *Rev;Edge(){}Edge(Node *from, Node *to, Edge *next):From(from),To(to),Next(next){}
}*_edges[MAX_EDGE];int _vCount, _eCount, DfsCnt;
vector<vector<Node*>> Blocks;
vector<Node*> Stack;void Init(int n) {memset(_nodes, 0, sizeof(_nodes));_eCount = DfsCnt = 0;Blocks.clear();Stack.clear();_vCount = n;
}Edge *NewEdge(Node *from, Node *to, Edge *next) {_eCount++;if (_edges[_eCount])*_edges[_eCount] = Edge(from, to, next);else_edges[_eCount] = new Edge(from, to, next);return _edges[_eCount];
}Edge *AddEdge(Node *from, Node *to) {Edge *e = NewEdge(from, to, from->Head);from->Head = e;return e;
}void Build(int uId, int vId, bool is2d) {Node *u = uId + _nodes, *v = vId + _nodes;u->Id = uId;v->Id = vId;Edge *e1 = AddEdge(u, v);if (is2d) {Edge *e2 = AddEdge(v, u);e1->Rev = e2;e2->Rev = e1;}
}void DeStack(Node *cut, Node *v) {Node *blockMember;vector<Node*> newBlock;Blocks.push_back(newBlock);while(!Stack.empty()) {//易忘点blockMember = Stack.back();Blocks.back().push_back(blockMember);Stack.pop_back();if (blockMember == v)break;};if (cut != NULL)Blocks.back().push_back(cut);
}void Dfs(Node *u,Node *root) {u->DfsN = u->Low = ++DfsCnt;Stack.push_back(u);for (Edge *e = u->Head; e; e = e->Next) {if (!e->To->DfsN) {Dfs(e->To,root);u->Low = min(u->Low, e->To->Low);if (u->DfsN <= e->To->Low)DeStack(u, e->To);}elseu->Low = min(u->Low, e->To->DfsN);}
}void SetBlock() {for (int i = 1; i <= _vCount; i++) {if (!_nodes[i].DfsN) {Stack.clear();Node *root = i + _nodes;Dfs(root, root);if (!Stack.empty())DeStack(root, NULL);}}
}bool HaveOrdCircle(Node *u, int curColor) {u->Color = curColor;for (Edge *e = u->Head; e; e = e->Next) {if (e->To->InBlock) {if (e->To->Color == -1) {if (HaveOrdCircle(e->To, !curColor))return true;}else if (e->To->Color == curColor)return true;}}return false;
}void SetTableMember() {int blockCnt = Blocks.size();for (int i = 0; i < blockCnt; i++) {int nodeCnt = Blocks[i].size();if (nodeCnt <= 2)continue;for (int j = 0; j < nodeCnt; j++) {Blocks[i][j]->InBlock = true;Blocks[i][j]->Color = -1;}for (int j = 0; j < nodeCnt; j++)if (HaveOrdCircle(Blocks[i][0], 0))for (int j = 0; j < nodeCnt; j++)Blocks[i][j]->InTable = true;for (int j = 0; j < nodeCnt; j++)Blocks[i][j]->InBlock = false;}
}int main() {
#ifdef _DEBUGfreopen("c:\\noi\\source\\input.txt", "r", stdin);freopen("c:\\noi\\source\\output.txt", "w", stdout);
#endifstatic bool hate[MAX_NODE][MAX_NODE];int totNode, totHate, a, b;while (~scanf("%d%d", &totNode, &totHate) && (totNode || totHate)) {Init(totNode);memset(hate, false, sizeof(hate));for (int i = 1; i <= totHate; i++) {scanf("%d%d", &a, &b);hate[a][b] = hate[b][a] = true;}for (int i = 1; i <= totNode; i++)for (int j = 1; j <= totNode; j++)if (i != j && !hate[i][j])Build(i, j, false);SetBlock();SetTableMember();int cnt = 0;for (int i = 1; i <= _vCount; i++)if (!_nodes[i].InTable)cnt++;printf("%d\n", cnt);}return 0;
}

  

转载于:https://www.cnblogs.com/headboy2002/p/8471238.html

POJ2942 Knights of the Round Table 点双连通分量 二分图判定相关推荐

  1. POJ 2942 Knights of the Round Table ★(点双连通分量+二分图判定)

    题意:找出图中不可能在奇圈中的点. [分析]注意到,在不同点双连通分量中的两个点,显然是不会存在圈的.那么这样,问题就划归为在点双连通分量中去找奇圈. [重要性质]在一个点双连通分量中,只要有任意一个 ...

  2. POJ2942 Knights of the Round Table 点双连通分量,逆图,奇圈

    题目链接: poj2942 题意: 有n个人,能够开多场圆桌会议 这n个人中,有m对人有仇视的关系,相互仇视的两人坐在相邻的位置 且每场圆桌会议的人数仅仅能为奇书 问有多少人不能參加 解题思路: 首先 ...

  3. Uvalive 3523 - Knights of the Round Table (双连通分量+二分图)

    题目链接 https://vjudge.net/problem/UVALive-3523 [题意] 有n个骑士经常举行圆桌会议,每次圆桌会议应至少有3个人参加且人数必须是奇数,相互憎恨的骑士不能坐在圆 ...

  4. UVA1364 Knights of the Round Table(双连通分量、二分图染色,超详细解释)

    整理的算法模板合集: ACM模板 UVA1364 Knights of the Round Table 题目中要求互相有憎恨关系的人不能坐在相邻的位置,一个圆桌可以很形象地看作是一个环,也就是说我们两 ...

  5. POJ - 2942 Knights of the Round Table(点双缩点+二分图判定)

    题目链接:点击查看 题目大意:国王要在圆桌上召开骑士会议,但是有若干对骑士之间互相憎恨.出于各种各样奇怪的原因,每次开会都必须有以下要求: 相互憎恨的两个骑士不能坐在相邻的两个位置 为了让投票表决议题 ...

  6. [POJ2942]:Knights of the Round Table(塔尖+二分图染色法)

    题目传送门 题目描述 亚瑟王要在圆桌上召开骑士会议,为了不引发骑士之间的冲突,并且能够让会议的议题有令人满意的结果,每次开会前都必须对出席会议的骑士有如下要求: 1.相互憎恨的两个骑士不能坐在直接相邻 ...

  7. POJ2942 UVA1364 Knights of the Round Table 圆桌骑士

    POJ2942 洛谷UVA1364(博主没有翻墙uva实在是太慢了) 以骑士为结点建立无向图,两个骑士间存在边表示两个骑士可以相邻(用邻接矩阵存图,初始化全为1,读入一对憎恨关系就删去一条边即可),则 ...

  8. 【POJ - 2942】Knights of the Round Table(点双连通分量,二分图判断奇环奇圈)

    题干: Being a knight is a very attractive career: searching for the Holy Grail, saving damsels in dist ...

  9. 如果圆桌骑士有特殊情况(Knights of the Round Table)

    题目描述 Being a knight is a very attractive career: searching for the Holy Grail, saving damsels in dis ...

最新文章

  1. 问题集锦(1-10)
  2. 开发日记-20190707 关键词 读书笔记 《Perl语言入门》Day 4
  3. iOS开发中标签控制器的使用——UITabBarController
  4. Python入门100题 | 第041题
  5. python zipfile 模块下中文乱码 '╡┌╥╗╒┬_╒╨▒Ω╣½╕µ.docx'
  6. 第三方应用如何在SAP Kyma上进行服务注册
  7. python全栈-Day 1
  8. 如何成为一名合格的数据分析师
  9. Shadow DOM系列1-简介
  10. Atitt 常见的拖欠费用的公司与个人黑名单 陕西西部安全网 2007-2008 西安 一个月 25000 西安盛世涅槃 2007 西安 1m 25000 Sicyi msc 2007 n
  11. 计算H时M分S秒以后是_泵所需轴功率的计算方式
  12. WebView load**方法 加载资源【总结】
  13. 【安全研究】免杀对抗之源码免杀
  14. Python爬虫——爬虫是什么都可以爬的吗?Robots协议!
  15. 数据库:常用数据库的创建
  16. 央企招聘:国家水利部直属单位2023公开招聘
  17. MWeb for Mac 3.2.0 专业级的markdown编辑器
  18. 电脑微信linux安装程序,Ubuntu安装Linux网页版微信
  19. 免费天气预报插件jquery版本
  20. 漫画:为什么程序员没有女朋友?

热门文章

  1. linux中fork()函数与vfork()函数的区别
  2. Java对象容器——Hash表/散列表
  3. 两个数从大到小排列输出
  4. Spring Security --SecurityConfig的详细配置
  5. SpringBoot 配置绑定
  6. zcmu-1644 多连块拼图
  7. Deep Residual Learning for Image Recognition(ResNet)论文翻译及学习笔记
  8. 区块链世界中的IOTA:它是什么样的存在?
  9. Android AOP之字节码插桩
  10. Android动态加载技术三个关键问题详解