POJ 2942
LUOGU SP2878
Description
Being a knight is a very attractive career: searching for the Holy Grail, saving damsels in distress, and drinking with the other knights are fun things to do. Therefore, it is not very surprising that in recent years the kingdom of King Arthur has experienced an unprecedented increase in the number of knights. There are so many knights now, that it is very rare that every Knight of the Round Table can come at the same time to Camelot and sit around the round table; usually only a small group of the knights isthere, while the rest are busy doing heroic deeds around the country.
Knights can easily get over-excited during discussions-especially after a couple of drinks. After some unfortunate accidents, King Arthur asked the famous wizard Merlin to make sure that in the future no fights break out between the knights. After studying the problem carefully, Merlin realized that the fights can only be prevented if the knights are seated according to the following two rules:
The knights should be seated such that two knights who hate each other should not be neighbors at the table. (Merlin has a list that says who hates whom.) The knights are sitting around a roundtable, thus every knight has exactly two neighbors.
An odd number of knights should sit around the table. This ensures that if the knights cannot agree on something, then they can settle the issue by voting. (If the number of knights is even, then itcan happen that '‘yes"and’'no" have the same number of votes, and the argument goes on.)
Merlin will let the knights sit down only if these two rules are satisfied, otherwise he cancels the meeting. (If only one knight shows up, then the meeting is canceled as well, as one person cannot sit around a table.) Merlin realized that this means that there can be knights who cannot be part of any seating arrangements that respect these rules, and these knights will never be able to sit at the Round Table (one such case is if a knight hates every other knight, but there are many other possible reasons). If a knight cannot sit at the Round Table, then he cannot be a member of the Knights of the Round Table and must be expelled from the order. These knights have to be transferred to a less-prestigious order, such as the Knights of the Square Table, the Knights of the Octagonal Table, or the Knights of the Banana-Shaped Table. To help Merlin, you have to write a program that will determine the number of knights that must be expelled.
Input
The input contains several blocks of test cases. Each case begins with a line containing two integers 1 ≤ n ≤ 1000 and 1 ≤ m ≤ 1000000 . The number n is the number of knights. The next m lines describe which knight hates which knight. Each of these m lines contains two integers k1 and k2 , which means that knight number k1 and knight number k2 hate each other (the numbers k1 and k2 are between 1 and n ).
The input is terminated by a block with n = m = 0 .
Output
For each test case you have to output a single integer on a separate line: the number of knights that have to be expelled.
Sample Input
5 5
1 4
1 5
2 5
3 4
4 5
0 0
Sample Output
2
Hint
Huge input file, ‘scanf’ recommended to avoid TLE.
Source
Central Europe 2005
题意翻译
题目大意
有n个骑士经常举行圆桌会议,商讨大事。每次圆桌会议至少有3个骑士参加,且相互憎恨的骑士不能坐在圆桌的相邻位置。如果发生意见分歧,则需要举手表决,因此参加会议的骑士数目必须是大于1的奇数,以防止赞同和反对票一样多。知道那些骑士相互憎恨之后,你的任务是统计有多少骑士不可能参加任何一个会议。
输入数据
包含多组数据,每组数据格式如下: 第一行为两个整数n和m(1<=n<=1000, 1<=m<=10^6)。 以下m行每行包含两个整数k1和k2(1<=k1,k2<=n),表示骑士k1和k2相互憎恨。 输入结束标记为n=m=0
输出数据
对于每组数据,在一行中输出一个整数,即无法参加任何会议的骑士个数。
亚瑟王要在圆桌上召开骑士会议,为了不引发骑士之间的冲突,并且能够让会议的议题有令人满意的结果,每次开会前都必须对出席会议的骑士有如下要求:
1、 相互憎恨的两个骑士不能坐在直接相邻的2个位置;
2、 出席会议的骑士数必须是奇数,这是为了让投票表决议题时都能有结果。
如果出现有某些骑士无法出席所有会议(例如这个骑士憎恨所有的其他骑士),则亚瑟王为了世界和平会强制把他剔除出骑士团。
现在给定准备去开会的骑士数n,再给出m对憎恨对(表示某2个骑士之间使互相憎恨的),问亚瑟王至少要剔除多少个骑士才能顺利召开会议?
注意:1、所给出的憎恨关系一定是双向的,不存在单向憎恨关系。
2、由于是圆桌会议,则每个出席的骑士身边必定刚好有2个骑士。即每个骑士的座位两边都必定各有一个骑士。
3、一个骑士无法开会,就是说至少有3个骑士才可能开会。
综合性非常强的图论题,在说解题报告之前,我建议大家先对以下内容有所认识,否则本题是很难做下去的:
1、补图 的定义
2、双连通分量 的定义
3、二分图 的定义
4、奇圈 的定义
5、判定一个图是否为二分图的方法:交叉染色法
6、Tarjan算法
上述知识点用于解决本题的重要性与其编号呈正相关。
最后要说一下的是,或者大家在看我这篇解题报告之前,已经看过其他解题报告,都不谋而合地给出了求割点的方法。
这里我声明一下:本题不需要求割点,我们仅仅是利用了Tarjan算法判定出现割点的过程以及条件而已,但我不建议用Tarjan算法去求无向图的割点,Tarjan算法只适用于求有向图的割点,直接套Tarjan模板用来求无向图割点会出错的。要求无向图的割点建议去看看刘汝佳的《算法艺术与信息学竞赛》,那里有详细方法。这些话与本题无关,我就不多说了。
为了方便大家做题,我这里大概第说一下上述的6个知识点,注意下述是我通俗的理解,不是标准定义:
图G的补图~G就是把图G原有的边全部删去,原本不存在的边全部连上。
简单来说,无向图G如果是双连通的,那么至少要删除图G的2个结点才能使得图G不连通。换而言之,就是图G任意2个结点之间都存在两条以上的路径连接(注意:路径不是指直接相连的边),那么双连通分量就是指无向图G的子图G’是双连通了。
二分图又叫二部图,这个百度百科有定义,了解一下二分图是什么样子的可以了,无需深入去了解。不懂得同学等到做二分图的题目时再认真学吧。
用一条线把奇数个点串连起来,所得到的闭合的圈就是奇圈了。其实奇圈就是有奇数个顶点的环。
初始化所有结点为无色(颜色0)状态,用DFS遍历一个图G的同时,顺便对结点染色(只染1、2色),注意遍历过的结点还可以再遍历重新上色。让遍历到某个时候在对结点t染色时,发现边s->t的另一个结点s已染色,且s的颜色与当前正在对t染的颜色相同,那么图G必定不是二分图。
这是因为想象一下二分图就像是河的两岸有两排结点,每染色一次则过河一次,那么相同颜色的结点必定在同一侧。一旦出现异侧有相同颜色的结点,就可以说明图G不是二分图了。
我希望大家主要去学习一下这个算法的基本原理,尤其是DFN数组和Low数组,还有什么是深搜树,什么是树枝边,什么是后向边。
学习一下Tarjan算法求割点的过程(注意我上文是建议大家不要用Tarjan算法去求解割点的题,但不是让大家不要看它求割点的过程),因为这个过程是求双连通分量的关键。
而如果想很好地了解Tarjan算法求割点的过程,还是建议先去看刘汝佳的《算法艺术与信息学竞赛》P285页,然后去做一下POJ1523(纯粹求割点的题)找点感觉。
只要弄懂了刘汝佳的方法,再看Tarjan算法就非常容易理解了。
给几个传送门:
有关图论的知识点的定义
byvoid
我的POJ1523解题报告
小優YoU
有了上述知识支撑,可以开始解题了:
1、 利用m对憎恨对构造图G,则图G中有边相连的两个点表示这2个骑士互相憎恨。
2、 构造图G的补图G,则图G中有边相连的两个点表示这2个骑士可以坐在相邻位置。
3、 在图~G中,可能存在某些点的度数<=1,就是说这些骑士旁边最多只能坐另一个骑士,根据圆桌的座位要求每个骑士k的座位两边都必定各有一个骑士(k度数==2),那么我们认为这些度数<=1的点是孤立的或者是单连通的,也就是说他们不在圆桌的“环”内。
例如上图,我们利用图G构造补图~G后,显然骑士1的度=0,他是孤立的、不连通的;骑士5的度=1,他是单连通的;骑士{2,3,4}则构成一个双连通分量,他们正在圆桌“环”内开会。显然度数<=1的骑士1和骑士5都在环外,不满足出席会议的条件,亚瑟王为了维护世界和平自然会把这2人驱逐出骑士团。
4、 现在问题是,我们怎么才能知道哪些骑士在环外?
我们可以把问题转化为,我们怎么才能知道哪些骑士在环内?显然在环内的所有结点都是双连通的,我们可以通过Tarjan算法求双连通分量。注意,补图~G可能有几个双连通子图,即它可能有不止一组双连通分量,而Tarjan算法是一组一组双连通分量求出来的,因此每求出一组双连通分量我们就要马上处理一组。
下面都是针对某一组双连通分量的处理。
5、 骑士在双连通分量内(在环内),并不能就此就说明它可以出席会议了,因为假如这个骑士所在的双连通分量,不是一个奇数顶点的环(奇圈),而是一个偶数顶点的环,那么这个双连通分量内的全部骑士都要被亚瑟王开除。
6、 那么怎样判断一个双连通分量是奇圈呢?
首先我们要接受两条定理,想知道证明过程的可以上网找,这里不证明:
(1) 如果一个双连通分量内的某些顶点在一个奇圈中(即双连通分量含有奇圈),那么这个双连通分量的其他顶点也在某个奇圈中;
(2) 如果一个双连通分量含有奇圈,则他必定不是一个二分图。反过来也成立,这是一个充要条件。
由于双连通分量也是一个图,那么要判断双连通分量是否为奇圈,只需判断这个双连通分量是否为一个二分图,而要判断一个图是否为二分图,就用交叉染色法!
7、 显然所有在奇圈中的骑士,都是允许出席会议的,而由于可能有部分骑士允许出席一个以上的会议(即他们是2个以上的奇圈的公共点),那么为了避免重复统计人数,当我们判断出哪些骑士允许出席会议时,就把他们做一个标记(相同的骑士只做一个标记)。最后当Tarjan算法结束后,我们统计一下被标记的人数有多少,再用总人数减去这部分人,剩下的就是被亚瑟王剔除的人数了。
说到这里,本题就已经解出来了,所有知识点都被联系起来了。老实说做完这题还很有成就感的O(∩_∩)O哈!
转自小優YoU
#include
using namespace std;
const int maxn=1e3+10,maxm=2e6+10;
template<typename T>inline void read(T &x)
{
x=0;
T f=1,ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
int ver[maxm],Next[maxm],head[maxn],len;
inline void add(int x,int y)
{
ver[++len]=y,Next[len]=head[x],head[x]=len;
}
int dfn[maxn],low[maxn],id;
int Stack[maxn],top,cnt;
vector<int>dcc[maxn];
inline void tarjan(int x,int root)
{
dfn[x]=low[x]=++id;
Stack[++top]=x;
if (x==root && head[x]==0)// 孤立点
{
dcc[++cnt].push_back(x);
return ;
}
for (int i=head[x];i;i=Next[i])
{
int y=ver[i];
if (!dfn[y])
{
tarjan(y,root);
low[x]=min(low[x],low[y]);
if (low[y]>=dfn[x])
{
int k;
++cnt;
do
{
k=Stack[top--];
dcc[cnt].push_back(k);
} while (k!=y);
dcc[cnt].push_back(x);
}
}
else
low[x]=min(low[x],dfn[y]);
}
}
bool flag,hate[maxn][maxn];
int c[maxn],v[maxn],able[maxn],now,n,m;;
inline void dfs(int x,int color)
{
c[x]=color;
for (int i=head[x];i;i=Next[i])
{
int y=ver[i];
if (v[y]^now) continue;
if (c[y] && c[y]==color)
{
flag=1;
return ;
}
if (!c[y]) dfs(y,3-color);
}
}
int main()
{
while (~scanf("%d %d",&n,&m)&&n)
{
memset(v,0,sizeof(v));
memset(able,0,sizeof(able));
memset(dfn,0,sizeof(dfn));
memset(head,0,sizeof(head));
len=id=top=cnt=0;
for (int i=1;i<=n;++i) dcc[i].clear();
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
hate[i][j]=0;
for (int i=1;i<=m;++i)
{
int x,y;
read(x);read(y);
if (x==y) continue;
hate[x][y]=hate[y][x]=1;
}
// 建补图
for (int i=1;i<n;++i)
for (int j=i+1;j<=n;++j)
if (!hate[i][j])
add(i,j),add(j,i);
// 求点双连通分量
for (int i=1;i<=n;++i)
if (!dfn[i]) tarjan(i,i);
// 判断每个点双是否包含奇环
for (int i=1;i<=cnt;++i)
{
now=i;
for (int j=0;j<dcc[i].size();++j)
v[dcc[i][j]]=now,c[dcc[i][j]]=0;
flag=0;
dfs(dcc[i][0],1);
if (flag)
for (int j=0;j<dcc[i].size();++j)
able[dcc[i][j]]=1;
}
int ans=0;
for (int i=1;i<=n;++i)
if (!able[i]) ++ans;
printf("%d\n",ans);
}
return 0;
}