您的位置:首页 > 其它

POJ 2942 Knights of the Round Table

2013-07-21 20:25 309 查看
题目描述:
武士是一个十分吸引人的职业,因此近年来Arthur王国武士的数量得到了空前的增长。武士
在讨论事情时很容易激动,特别是喝了酒以后。在发现一些不幸的打斗后,Arthur国王要求智者
Merlin确保将来不会发生武士的打斗。
Merlin在仔细研究这个问题后,他意识到如果武士围着圆桌坐下,要阻止打斗则必须遵循以
下两个规则:
(1) 任何两个互相仇视的武士不能挨着坐,Merlin有一张清单,列出了互相仇视的武士,注意,
武士是围着圆桌坐下的,每个武士有两个相邻的武士。
(2) 围着圆桌坐下的武士数量必须为奇数个。这将能保证当武士在争论一些事情时,能通过投
票的方式解决争端。而如果武士数量为偶数个,则可能会出现赞同和反对的武士数量一样。
如果以上两个条件满足,Merlin将让这些武士围着圆桌坐下,否则他将取消圆桌会议。如果
只有一个武士到了,Merlin也将取消会议,因为一个武士无法围着圆桌坐下。Merlin意识到如果
遵守以上两个规则,可能会使某些武士不可能被安排坐下,一个例子是如果一个武士仇视其他每
个武士。如果一个武士不可能被安排坐下,他将被从武士名单中剔除掉。你的任务是帮助Merlin
判断有多少个武士将会被剔除掉。
输入描述:
输入文件中包含多个测试数据。每个测试数据第1行为两个整数n和m,1≤n≤1000,1≤
m≤1000000,n表示武士数目,n个武士编号为1~n,m表示武士相互仇视的对数。接下来有
m行,描述了相互仇视的每对武士,每行为两个整数k1和k2,表示武士k1和k2相互仇视。
n = m = 0表示输入结束。
输出描述:
对输入文件中的每个测试数据,输出一行,为从名单中被剔除掉的武士数目。

// 开始理解错题目意思了 一直以为要大家同时坐下去 问有哪些人不能坐   发现自己二了
// 那样真的怎么动手都不知道
// 求无向图的双连通分量 (tarjan算法)  并判断奇圈(通过奇偶染色)
// 主要是只要是点数大于2的非二分图 而且双连通  那么每个点坑定肯定会在某个奇圈里面
// 首先要理解 点数大于2的非二分图 而且双连通 那么里面肯定含有奇圈 可以用反证法法去理解
// 那么 某个在这个奇圈外的点 u 与奇圈内的两点u1 u2有2条路径 而且这2条路径完全不重叠,所经过的点都不同 不然就有割点存在了 (由双连通性质决定)
// 那么 由于 u1 u2两点在奇圈内距离一奇一偶 那么就可让u这个点进入一个奇圈
// 所以 点数大于2 非二分图 就是可以一起开会的了
// 还有 二分图不存在奇圈 也可以用反证法去理解

#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <math.h>
#include <stdio.h>
#include <string.h>
using namespace std;
#define MOD 1000000007
#define maxn 1000010
#define maxm 1010
struct Edge{
int to;
int next;
Edge(){};
Edge(int u,int v){to=u;next=v;}
}E[maxn];
int V[maxm],num;
int pre[maxm];
int dfst,bcc;
stack<Edge> S;
vector<int> bccno[maxm];
int belong[maxm];
int clor[maxm];
bool ans[maxm];
bool A[maxm][maxm];
void init(){
dfst=0;
num=0;
bcc=0;
while(!S.empty())
S.pop();
memset(V,-1,sizeof(V));
memset(pre,0,sizeof(pre));
memset(ans,0,sizeof(ans));
memset(A,0,sizeof(A));
memset(belong,0,sizeof(belong));
}
void add(int u,int v){
E[num].to=v;
E[num].next=V[u];
V[u]=num++;

E[num].to=u;
E[num].next=V[v];
V[v]=num++;
}
int dfs(int u,int fa){
int lowu; // printf("?");
Edge x;
lowu=pre[u]=++dfst;
int v,e;
for(e=V[u];e!=-1;e=E[e].next){
v=E[e].to;
if(!pre[v]){
S.push(Edge(u,v));// 压边进去
int lowv=dfs(v,u);
//   printf(" %d %d\n",u,lowv);
lowu=min(lowu,lowv);
if(lowv>=pre[u]){
bcc++;bccno[bcc].clear();
for(;;){  // 将形成双连通的部分的点放在一起
x=S.top();S.pop();
//  printf("%d %d ",x.to,x.next);
if(belong[x.to]!=bcc){bccno[bcc].push_back(x.to);belong[x.to]=bcc; }
if(belong[x.next]!=bcc){bccno[bcc].push_back(x.next);belong[x.next]=bcc; }
if(x.to==u&&x.next==v) break;
}
//  printf("  u= %d\n",bcc);
}
}
else if(v!=fa) lowu=min(lowu,pre[v]);
}
return lowu;
}
bool color(int u,int kind){
int e,v;
for(e=V[u];e!=-1;e=E[e].next){
v=E[e].to;
if(belong[v]!=kind) continue;//printf("kind %d %d \n",u,v);
if(clor[v]==clor[u]) return false;
if(!clor[v]){
clor[v]=3-clor[u];
if(!color(v,kind)) return false;
}
}
return true;
}
int main()
{
int n,m;
int u,v;
int i,j;
while(scanf("%d %d",&n,&m),n|m){
init();
for(i=1;i<=m;i++){
scanf("%d %d",&u,&v);
A[u][v]=1;
A[v][u]=1;
}
for(i=1;i<=n;i++)
for(j=i+1;j<=n;j++)
if(!A[i][j])
add(i,j);

for(i=1;i<=n;i++)
if(!pre[i])
dfs(i,0);
for(i=1;i<=bcc;i++){
memset(clor,0,sizeof(clor));
for(j=0;j<bccno[i].size();j++)
belong[bccno[i][j]]=i;// 处理 割点
u=bccno[i][0];
clor[u]=1;
if(!color(u,i)){//  printf("%d %d ",u,i);
for(j=0;j<bccno[i].size();j++) ans[bccno[i][j]]=true;
}
}
int out=n;
for(i=1;i<=n;i++)
if(ans[i]) out--;
printf("%d\n",out);

}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: