POJ2942:Knights of the Round Table——题解

http://poj.org/problem?id=2942

所写的tarjan练习题最难的一道。

说白了难在考得不是纯tarjan。

首先我们把仇恨关系处理成非仇恨关系的图,然后找双连通分量,在双连通分量里的点满足了任意一个人可以和两个(或以上)的人坐一起。

那么我们接下来要判断奇环。

发现性质:如果一个双连通分量有奇环,那么其中任意一点一定在某个奇环上。

也就是说,这些人拼一拼绝对能全部开会成功,我们把他们打上成功标志。

然后搜失败标志的人的个数即可。

判断奇环的方法显然二分图染色。

#include<stack>
#include<cstdio>
#include<cstring>
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
inline int read(){
    int x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*w;
}
const int maxn=1001;
struct node{
    int st;
    int to;
    int nxt;
}edge[2000001];
int cnt,head[maxn];
void add(int u,int v){
    cnt++;
    edge[cnt].st=u;
    edge[cnt].to=v;
    edge[cnt].nxt=head[u];
    head[u]=cnt;
    return;
}
bool dis[maxn][maxn];
bool ok[maxn];
int color[maxn];
int dfn[maxn];
int low[maxn];
bool inslt[maxn];
int t=0;
int n,m;
int numslt[maxn];
stack<int>q;
vector<int>slt[maxn];
int slt_cnt;
void tarjan(int u,int f){
    t++;
    dfn[u]=t;
    low[u]=t;
    for(int i=head[u];i;i=edge[i].nxt){
    int v=edge[i].to;
    if(!dfn[v]){
        q.push(i);
        tarjan(v,u);
        low[u]=min(low[u],low[v]);
        if(low[v]>=dfn[u]){
        slt_cnt++;
        slt[slt_cnt].clear();
        while(233){
            int num=q.top();
            q.pop();
            if(numslt[edge[num].st]!=slt_cnt){
            numslt[edge[num].st]=slt_cnt;
            slt[slt_cnt].push_back(edge[num].st);
            }
            if(numslt[edge[num].to]!=slt_cnt){
            numslt[edge[num].to]=slt_cnt;
            slt[slt_cnt].push_back(edge[num].to);
            }
            if(edge[num].to==v&&edge[num].st==u)break;
        }
        }
    }else if(f!=v){
        if(low[u]>dfn[v]){
        q.push(i);
        low[u]=dfn[v];
        }
    }
    }
    return;
}
bool draw(int u){
    bool ret=0;
    for(int i=head[u];i;i=edge[i].nxt){
    int v=edge[i].to;
    if(!inslt[v])continue;
    if(color[v]==-1){
        color[v]=1-color[u];
        ret|=draw(v);
    }else if(color[v]==color[u]){
        return 1;
    }
    }
    return ret;
}
void clr(){
    cnt=0;slt_cnt=0;
    while(!q.empty())q.pop();
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(numslt,0,sizeof(numslt));
    memset(head,0,sizeof(head));
    memset(dis,0,sizeof(dis));
    memset(ok,0,sizeof(ok));
    return;
}
int main(){
    n=read();
    m=read();
    while(n||m){
    clr();
    for(int i=1;i<=m;i++){
        int u=read();
        int v=read();
        dis[u][v]=dis[v][u]=1;
    }
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
        if(!dis[i][j]){
            add(i,j);
            add(j,i);
        }
        }
    }
    for(int i=1;i<=n;i++){
        if(!dfn[i]){
        tarjan(i,0);
        }
    }
    for(int i=1;i<=slt_cnt;i++){
        memset(inslt,0,sizeof(inslt));
        memset(color,-1,sizeof(color));
        int u;
        for(int j=0;j<slt[i].size();j++){
        u=slt[i][j];
        inslt[u]=1;
        }
        color[u]=0;
        if(draw(u)){
        for(int j=0;j<slt[i].size();j++){
            u=slt[i][j];
            ok[u]=1;
        }
        }
    }
    int ans=0;
    for(int i=1;i<=n;i++)if(!ok[i])ans++;
    printf("%d\n",ans);
    n=read();m=read();
    }
    return 0;
}

 

posted @ 2017-11-17 17:43  luyouqi233  阅读(267)  评论(0编辑  收藏  举报