Knights of the Round Table
update@2011-09-21
///头文件等等省略
#define MAX 1010
#define mymin(a,b) (a>b?b:a)
int graph[MAX][MAX];
int head[MAX];
struct node{
int u,v,next;
}edge[MAX*MAX+100];
int edge_count;
int expelled[MAX];
struct node point_dcc[MAX*MAX+100];
int head2[MAX];
int top;
struct edge_node{
int u,v;
edge_node(int uu,int vv){u = uu; v = vv;}
};
deque<edge_node> my_deque;
void add2(int u,int v){
point_dcc[edge_count].u = u;
point_dcc[edge_count].v = v;
point_dcc[edge_count].next = head2[u]; head2[u] = edge_count++;
}
int visited2[MAX];
int isBipartite(int uu,int colo){
//判断当前的点连通分量是是不是二分图
//如果是二分图则它含有奇数圈
//如果它含有奇数圈 则该 点双连通图 的所有点都能找到一个奇数圈使相应的点在某个奇数圈上
queue<int> my_queue;
my_queue.push(uu);visited2[uu] = colo;
while(!my_queue.empty()){
int u = my_queue.front(); my_queue.pop();
for(int i = head2[u];i != -1; i = point_dcc[i].next){
int v = point_dcc[i].v;
if(visited2[v] != -1){
if(visited2[u] == visited2[v]){
//有相邻两个节点的颜色是一样的
return 1;
}
}
else{visited2[v] = (!visited2[u]);my_queue.push(v);}
}
}
return 0;
}
void mark_knight(){
for(int i = 0;i < edge_count;i++){
int u = point_dcc[i].u, v = point_dcc[i].v;
expelled[u] = 0; expelled[v] = 0;
}
}
int getNum(int N){
int total = 0;//统计被踢掉的
for(int i = 1;i <= N;i++){
if(expelled[i])total++;
}
return total;
}
void get_Point_DCC(int uu,int vv){
//将栈中的点双连通分量弹出来
memset(head2,-1,sizeof(head2)); edge_count = 0;
struct edge_node p(0,0);
do{
p = my_deque.back();my_deque.pop_back();
add2(p.u,p.v);
}while(!(p.u == uu && p.v == vv));
memset(visited2,-1,sizeof(visited2));
if(isBipartite(point_dcc[0].u,0)){
mark_knight();//把不需要踢掉的knight标记下
}
}
/**
*tarjan 算法求双连通分量
*/
int dfs_order[MAX];
int lowest[MAX];
int visited[MAX];
int mark_num;
void Tarjan_DCC(int u,int father){
dfs_order[u] = lowest[u] = mark_num++;visited[u] = 1;
for(int i = head[u];i != -1;i = edge[i].next){
int v = edge[i].v;
if(!visited[v]){
my_deque.push_back(edge_node(u,v));
Tarjan_DCC(v,u);
lowest[u] = mymin(lowest[u],lowest[v]);
if(dfs_order[u] <= lowest[v]){
get_Point_DCC(u,v);
}
}
else{
if(v != father){
lowest[u] = mymin(lowest[u],dfs_order[v]);
if(dfs_order[u] > dfs_order[v]){
my_deque.push_back(edge_node(u,v));
}
}
}
}
}
void add(int a,int b){
edge[edge_count].u = a;
edge[edge_count].v = b;
edge[edge_count].next = head[a];
head[a] = edge_count++;
}
void init(){
memset(graph,0,sizeof(graph));
edge_count = 0;
memset(head,-1,sizeof(head));
top = 0;
memset(visited,0,sizeof(visited));
mark_num = 1;
memset(expelled,1,sizeof(expelled));
my_deque.clear();
}
///main
int main(){
int N,M;
while(scanf("%d%d",&N,&M) && N != 0){
init();
for(int i = 1;i <= M;i++){
int a,b;scanf("%d%d",&a,&b);
graph[a][b] = graph[b][a] = 1;
}
for(int i = 1;i < N;i++){
for(int j = i+1;j <= N;j++){
if(graph[i][j])continue;
add(i,j); add(j,i);//双向的边 也就是无向边
}
}
for(int i = 1;i <= N;i++){
if(visited[i])continue;
Tarjan_DCC(i,-1);
}
printf("%d\n",getNum(N));
}
return 0;
}
题目链接:
Knights of the Round Table调了一晚上终于AC了,重点是求双连通分量的tarjan算法,之后就是弹出双连通分量,判断是否含有奇数圈,这些都很繁琐,网上很多解题报告就不多说了。判断奇数圈用的队列之前是全局变量,而且用完后没有清空 导致无限WA...,各种调啊..最近的代码都很长 很繁琐..