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

弄了一天 终于搞定

伦理知识我建议看这里 http://blog.csdn.net/lyy289065406/article/details/6756821

讲的很详细 不过代码的风格不是很好接受

刚开始我以为是所有的骑士全围着一张桌子坐

后来才知道某个骑士只要能和任意其他符合要求的骑士组成一个会议小组他就可以留下

开会条件:

a 骑士的数量必须是奇数而且不能是1

b 他的两个邻居不是他仇恨的

给出原图后建补图

求点的双联通

对补图每求一个割点就对其进行操作

出栈操作 但是联通根点不出栈 (因为它可能属于其他的联通图)

对其联通图进行遍历 对所有的奇环内的点进行标记 他们是可留下的

整个图有可能不联通 也就是说图会有好几块 要注意

代码及其注释:

#include<iostream>
#include<cstring>
#include<stack>
#include<cstdio>
#include<queue>

using namespace std;

const int N=1005;
struct node
{
    struct tt *next;
}mem[N];
struct tt
{
    struct tt *next;
    int j;
};
bool visited[N];
stack<int>str;
bool in[N];
int low[N];
int deep;
int dfn[N];
bool uppoint[N];
bool link[N][N];
int color[N];
bool save[N];
void build(int i,int j)
{
    struct tt *t=new tt;
    t->j=j;
    t->next=mem[i].next;
    mem[i].next=t;
}
void Clear(int n)//每次对邻接表进行清理
{
   struct tt *t;
   for(int i=1;i<=n;++i)
   {
       while(mem[i].next!=NULL)
       {
           t=mem[i].next;
           mem[i].next=t->next;
           delete t;
       }
   }
}
bool dfs(int x,int c)//交叉染色看是否有奇环
{
    color[x]=c;
    struct tt *t=mem[x].next;
    while(t!=NULL)
    {
        if(uppoint[t->j])
        {
            if(color[t->j]==0)
            {
                if(dfs(t->j,-c))
                return true;
            }else if(color[t->j]==color[x])
            {
                return true;
            }
        }
        t=t->next;
    }
    return false;
}
bool Savepoint(int pre,int x)//有奇环的话对其所有的环进行 标记(留下) 但一些非环分支不标记
{
    color[x]=1;
    struct tt *t=mem[x].next;
    bool ok=false;
    while(t!=NULL)
    {
        if(uppoint[t->j])
        {
            if(color[t->j]==1)
            {
                if(t->j!=pre)
                ok=true;
            }
            else
            {
                if(Savepoint(x,t->j))
                ok=true;
            }
        }
        t=t->next;
    }
    if(ok)
    save[x]=true;
    return ok;
}
void find(int x)//找可留下的骑士
{
    memset(uppoint,false,sizeof(uppoint));//标记是否属于此联通图
    uppoint[x]=true;
    while(str.top()!=x)
    {
        in[str.top()]=false;
        uppoint[str.top()]=true;
        str.pop();
    }
    memset(color,0,sizeof(color));//染色数组
    if(dfs(x,1))//有奇环
    {
      memset(color,0,sizeof(color));
      Savepoint(x,x);//对此连通图内所有可构成环的点进行标记
    }
}
void Tanjan(int pre,int x)
{
    ++deep;
    dfn[x]=low[x]=deep;
    visited[x]=true;
    in[x]=true;
    str.push(x);
    struct tt *t=mem[x].next;
    while(t!=NULL)
    {
        if(visited[t->j]==false)
        {
            Tanjan(x,t->j);
            low[x]=min(low[x],low[t->j]);
            if(low[x]==dfn[x])//每出现割点就找可留下的骑士
            {
               find(x);
            }
        } else
        if(in[t->j]&&pre!=t->j)
        {
            low[x]=min(low[x],dfn[t->j]);
        }
        t=t->next;
    }
}
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0&&m==0)
        break;
        memset(link,false,sizeof(link));
        while(m--)
        {
            int i,j;
            scanf("%d%d",&i,&j);
            link[i][j]=link[j][i]=true;//保存原图
        }
        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=n;++j)
            {
                if(i!=j&&link[i][j]==false)
                {
                    build(i,j);//建补图
                }
            }
        }
        memset(visited,false,sizeof(visited));
        memset(in,false,sizeof(in));
        memset(save,false,sizeof(save));
        deep=0;
        for(int i=1;i<=n;++i)
        {
            if(visited[i]==false)//防止补图不联通
            {
                 while(!str.empty())
                 str.pop();
                 Tanjan(i,i);
            }
        }
        int sum=0;
        for(int i=1;i<=n;++i)
        {
            if(!save[i])
            ++sum;
        }
        printf("%d\n",sum);
        Clear(n);
    }
    return 0;
}

 

 

posted on 2012-05-31 19:41  夜->  阅读(174)  评论(0编辑  收藏  举报