POJ2942 Knights of the Round Table 点双连通分量 二分图判定
题目大意
有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);
}
else
u->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 _DEBUG
freopen("c:\\noi\\source\\input.txt", "r", stdin);
freopen("c:\\noi\\source\\output.txt", "w", stdout);
#endif
static 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;
}
POJ2942 Knights of the Round Table 点双连通分量 二分图判定的更多相关文章
- POJ2942 Knights of the Round Table[点双连通分量|二分图染色|补图]
		
Knights of the Round Table Time Limit: 7000MS Memory Limit: 65536K Total Submissions: 12439 Acce ...
 - poj 2942 Knights of the Round Table(点双连通分量+二分图判定)
		
题目链接:http://poj.org/problem?id=2942 题意:n个骑士要举行圆桌会议,但是有些骑士相互仇视,必须满足以下两个条件才能举行: (1)任何两个互相仇视的骑士不能相邻,每个骑 ...
 - POJ2942   Knights of the Round Table      点双连通分量,逆图,奇圈
		
题目链接: poj2942 题意: 有n个人,能够开多场圆桌会议 这n个人中,有m对人有仇视的关系,相互仇视的两人坐在相邻的位置 且每场圆桌会议的人数仅仅能为奇书 问有多少人不能參加 解题思路: 首先 ...
 - UVALive-3523 Knights of the Round Table (双连通分量+二分图匹配)
		
题目大意:有n个骑士要在圆桌上开会,但是相互憎恶的两个骑士不能相邻,现在已知骑士们之间的憎恶关系,问有几个骑士一定不能参加会议.参会骑士至少有3个且有奇数个. 题目分析:在可以相邻的骑士之间连一条无向 ...
 - 【POJ】2942 Knights of the Round Table(双连通分量)
		
http://poj.org/problem?id=2942 各种逗.... 翻译白书上有:看了白书和网上的标程,学习了..orz. 双连通分量就是先找出割点,然后用个栈在找出割点前维护子树,最后如果 ...
 - [POJ2942]Knights of the Round Table(点双+二分图判定——染色法)
		
建补图,是两个不仇恨的骑士连边,如果有环,则可以凑成一桌和谐的打麻将 不能直接缩点,因为直接缩点求的是连通分量,点双缩点只是把环缩起来 普通缩点 ...
 - poj2942 Knights of the Round Table[点双+二分图染色]
		
首先转化条件,把无仇恨的人连边,然后转化成了求有哪些点不在任何一个奇环中. 一个奇环肯定是一个点双,所以想到处理出所有点双,但是也可能有的点双是一个偶环,有的可能是偶环和奇环混杂,不好判. 考察奇环性 ...
 - POJ 2942 Knights of the Round Table(双连通分量)
		
http://poj.org/problem?id=2942 题意 :n个骑士举行圆桌会议,每次会议应至少3个骑士参加,且相互憎恨的骑士不能坐在圆桌旁的相邻位置.如果意见发生分歧,则需要举手表决,因此 ...
 - 【POJ 2942】Knights of the Round Table(双联通分量+染色判奇环)
		
[POJ 2942]Knights of the Round Table(双联通分量+染色判奇环) Time Limit: 7000MS Memory Limit: 65536K Total Su ...
 
随机推荐
- 刷题总结——蜥蜴(ssoj网络流)
			
题目: 题目背景 SCOI2007 DAY1 T3 题目描述 在一个 r 行 c 列的网格地图中有一些高度不同的石柱,一些石柱上站着一些蜥蜴,你的任务是让尽量多的蜥蜴逃到边界外.每行每列中相邻石柱的距 ...
 - 【SDOI2018】战略游戏(同时普及虚树)
			
先看一道虚树普及题:给你一棵 $n$ 个点的树,$m$ 次询问,每次询问给你 $k$ 个关键点,求把这些点都连起来的路径并的最短长度.$1\le n,m\le 100000,\space 1\le \ ...
 - 使用腾讯互动直播 遇到的坑 'GLIBC_2.14' not found 问题解决
			
第一.查看系统glibc版本库 strings /lib64/libc.so.6 |grep GLIBC_ 这里我们可以看到系统中最新的版本是2.12,这里我们升级2.14. 第二.下载和安装glib ...
 - Numpy 花式索引
			
记住:花式索引跟切片不一样,它总是将数据复制到新数组中. 一 给定一个列表,返回索引为1,3,4,5,6的数组 2 针对二维数组 需要注意的一点是,对于花式索引.对照下后面的两种方式,查询结果的不同.
 - 你需要的130个vim命令
			
参考文章:http://www.oschina.net/news/43167/130-essential-vim-commands,其中有些我以为需要补充的及时补充或修改 从 1970 年开始,vi ...
 - Redis数据结构之简单动态字符串
			
Redis没有直接使用C语言传统的字符串表示(以空字符结尾的字符数组), 而是自己构建了一种名为简单动态字符串(simple dynamic string,SDS)的抽象类型, 并将SDS用作Redi ...
 - Yii2之创建定时任务
			
yii开发的项目需要使用定时任务其实也可以使用一些单独的脚本文件来完成,但若是定时任务代码中需要使用到项目中的一些类,特别是需要使用应用对象Yii::$app的时候,单独的脚本想要完成就比较麻烦了.这 ...
 - BZOJ——3296: [USACO2011 Open] Learning Languages
			
http://www.lydsy.com/JudgeOnline/problem.php?id=3296 Time Limit: 5 Sec Memory Limit: 128 MBSubmit: ...
 - python入门示例程序
			
该实例是raspi和dsp电机运动控制板的串口uart通信: import serial class SerialHandler(): ''' raspi serial for communicati ...
 - Codeforces Gym 100286J Javanese Cryptoanalysis 傻逼暴力
			
原题地址:http://codeforces.com/gym/100286/attachments/download/2013/20082009-acmicpc-northeastern-europe ...