您的位置:首页 > 其它

poj2942—Knights of the Round Table(点双联通分量的求解+染色法判断奇圈)

2017-07-25 11:52 435 查看
题目链接:传送门

Knights of the Round Table

Time Limit: 7000MS Memory Limit: 65536K
Total Submissions: 13273 Accepted: 4438
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


题目大意:有一个国王要召集一群武士开圆桌会议,但这群武士要想坐在一起开会,就得满足如下几个条件1.任何两个互相仇视的武士不能挨着坐2.围着圆桌坐下的武士数量必须是奇数个。最后问你有多少武士一定不能参加会议。

解题思路:首先不互相仇视的武士间互相建边,然后在图上找出所有的点双联通分量(即这些武士可能坐在一起),但坐在一起的武士数量要为奇数个,所以这里还要在求出的联通分量中判断下是否含有奇圈(即一个环的节点个数是奇数个)。将奇圈上的点标记一下,最后没有被标记的点就是一定不能去的武士。

某条性质:在一个节点大于2的双联通分量中,必定存在一个圈经过该联通分量的所有节点。如果这个圈是偶圈,其中包含某个奇圈,那必定还有一个奇圈经过所有剩下的点。

这里只要判断点联通分量中含有奇圈就行了,而不必要所有的点都在同一个奇圈上,因为题目问的是一定不能参加会议的武士。

点双联通分量求解:

维护一个栈,在dfs的过程中,每找到树上的一条边或回边,就把这条边加入栈中,如果遇到某个顶点u的子女顶点v满足dfn[u]<=low[v],就把边从栈中一条条取出,直到遇到边(u,v).

边双联通分量求解:

求出图中所有的割边,标记一下,然后再搜索一下就可以了

染色法求奇圈:

想想用这个方法判断一个图是否是二分图时,首先将一个点染成某种颜色,将与该点相连的边染成相对的颜色,将所有的点染完色后无冲突就是二分图,否则就是图中含有奇圈。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <vector>

using namespace std;

typedef long long ll;
typedef pair<int,int>PA;
const int N = 1000900;
const int M = 1090;
const int INF = 0x3fffffff;
const double eps = 1e-8;
const double PI = acos(-1.0);

/*
求点的双联通分量:
维护一个栈,每找到生成树的边或回边,就把这条边加入栈中
如果遇到某个顶点u的子女顶点v满足dfn[u]<=low[v],将边一条条取出,直到遇到边(u,v)

求边的双联通分量:
先找出所有的割边,标记一下,然后搜一下就行了
*/

struct Edge{
int node;
Edge*next;
}m_edge[N*2];
Edge*head[M];
int low[M],dfn[M],Flag[M],Ecnt;
int ret[M][M],color[M],tick[M],num,dep;
//num统计联通分量的数目,tick标记图中双联通分量的点
stack<PA>st;
vector<int>graph[M];

void init()
{
Ecnt = num = dep = 0;
//fill( Flag , Flag+M , 0 );
fill( head , head+M , (Edge*)0 );
for( int i = 0 ; i < M ; ++i ) graph[i].clear();

}

void mkEdge( int a , int b )
{
m_edge[Ecnt].node = b;
m_edge[Ecnt].next = head[a];
head[a] = m_
4000
edge+Ecnt++;
}

void tarjan( int u , int father )
{
Flag[u] = 1;
low[u] = dfn[u] = ++dep;
//st.push(u);
for( Edge*p = head[u] ; p ; p = p->next ){
int v = p->node;
if( ret[u][v] != 2 ){
st.push(PA(u,v));
ret[u][v] = ret[v][u] = 2;
}
if( !Flag[v] ){
tarjan(v,u);
low[u] = min(low[u],low[v]);
//求点的双联通分量
if( low[v] >= dfn[u] ){
num++;
while( !st.empty() ){
int t1 = st.top().first;
int t2 = st.top().second;
graph[num].push_back(t1);
graph[num].push_back(t2);
st.pop();
if( (t1 == u && t2 == v) || (t1 == v && t2 == u) ) break;
}
}
}
//v是u的祖先,且(u,v)是一条回边
if( v != father && Flag[v] )
low[u] = min(low[u],dfn[v]);
}
}

//判断点双联通分量中是否含有奇圈
bool getColor( int u )
{
for( Edge*p = head[u] ; p ; p = p->next ){
int v = p->node;
//找双联通分量中的点
if( tick[v] ){
if( color[u] == color[v] ) return true;
if( color[v] == -1 ){
color[v] = color[u]^1;
if( getColor(v) ) return true;
}
}
}
return false;
}

//图不一定联通,获取所有的双联通分量
void getPoint( int n )
{
fill( Flag , Flag+M , 0 );
while( !st.empty() ) st.pop();
for( int i = 1 ; i <= n ; ++i ){
if( !Flag[i] ) tarjan( i , -1 );
}
}

int Cal( int n )
{
fill( Flag , Flag+M , 0 );
for( int i = 1 ; i <= num ; ++i ){
fill( tick , tick+M , 0 );
fill( color , color+M , -1 );
for( int j = 0 ; j < graph[i].size() ; ++j ){
tick[graph[i][j]] = 1;
}
int t = graph[i][0];
color[t] = 0;
if( getColor(t) )
for( int j = 1 ; j <= n ; ++j ) Flag[j] += tick[j];
}
int ans = 0;
for( int i = 1 ; i <= n ; ++i ){
if( !Flag[i] ) ans++;
}
return ans;
}

void Build( int n , int m )
{
int a,b;
memset( ret , 0 , sizeof(ret) );
for( int i = 0 ; i < m ; ++i ){
scanf("%d%d",&a,&b);
ret[a][b] = ret[b][a] = 1;
}
for( int i = 1 ; i <= n ; ++i ){
for( int j = i+1 ; j <= n ; ++j ){
//两个人不互相仇视
if( ret[i][j] == 0 ){
mkEdge(i,j);
mkEdge(j,i);
}
}
}
}

int main()
{
int n,m;
while( ~scanf("%d%d",&n,&m)&&n&&m ){
init();
Build(n,m);
getPoint(n);
printf("%d\n",Cal(n));
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐