圆桌骑士(双联通)

题意:很多骑士要围着一个桌子坐,并且一个桌子上只能坐奇数个骑士,但是有仇的两个人不能坐在一起。给你m组数据,每组数据表明了哪两个骑士是有仇的。所以现在问你有多少个骑士,无论怎么安排都不可能和别人坐在一起。

思路:只要两个骑士没有仇,就可以再它们之间连接一条边,这样的话构造一个图后,就相当于把所有情况都枚举了出来。然后再这个囊括了所有情况的图中,找到那些具有奇数个点的并且不是二分图的环路,那么剩下的不在这些环路中的点就是那些无论咋安排都不能和别人坐在一起的骑士。为什么要去找环,因为骑士围着桌子坐成一个圈,就是一个环路,为什么去找奇数点,因为只能坐奇数个骑士,为什么要是非二分图,因为二分图构成的环不可能存在奇数个点,这个我也是看题解自己在底下画了几个圆才验证得知的。

找环的方法:这道题当中,环就是那些个双连通分量,用蓝书上 找双连通分量的模板,然后再去判连通分量是否为二分图,这里判定模板在蓝书的基础上改造了,提前把一个连通分量里的所有点全部都标记一遍,然后 再去深搜判二分图。这么做的原因是因为,有可能很多连通分量是依靠某个割点交织在一起的,所以当你遍历到割点的时候它的子节点就不一定属于当前连通分量了,所以深搜的时候有必要提前判定一下下一个深搜的子节点是否是属于当前连通分量。 其余的蓝书模板都没有怎么变化,直接抄板子就可以了。下面上我的代码,一开始以为只用输入一组数据,就用链式前向星存的图,没想到是多组输入,每次还要清空,懒得改了。。。。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1010;
const int maxh=1e6+10;
int cnt=0;
int color[maxn],head[maxn],pre[maxn],iscut[maxn],bccno[maxn],dfs_clock,bcc_cnt,odd[maxn];
vector<int> bcc[maxn];//bcc数组用以表明某个双联通分量集合下有哪些点//bccco用以表明某个点属于哪个连通分量
int A[maxn][maxn];

struct Edge0{
  int u,v;
};

struct Edge{
  int to,next;
  int w;
}edge[maxh];

void add(int u,int v){
   edge[++cnt].to=v;edge[cnt].next=head[u];head[u]=cnt;
   //cout<<edge[cnt].to<<" "<<"\n";
}

bool bipartite(int u,int b)
{
    for(int i=head[u];i;i=edge[i].next){
        int v=edge[i].to;
        if(bccno[v]!=b) continue;
        if(color[v]==color[u]) return false;
        if(!color[v]){
            color[v]=3-color[u];
            if(!bipartite(v,b)) return false;
        }
    }
    return true;
}

stack<Edge0> S;
int dfs(int u,int fa){
    int lowu=pre[u]=++dfs_clock;
    int child=0;
    for(int i=head[u];i;i=edge[i].next)
    {
        int v=edge[i].to;
        Edge0 e=(Edge0){u,v};
        if(!pre[v]){
        S.push(e);child++;
        int lowv=dfs(v,u);
        lowu=min(lowu,lowv);
        if(lowv>=pre[u]){
            iscut[u]=true;
            bcc_cnt++;bcc[bcc_cnt].clear();
            for(;;){
                Edge0 x=S.top();S.pop();
                if(bccno[x.u]!=bcc_cnt){bcc[bcc_cnt].push_back(x.u);bccno[x.u]=bcc_cnt;}
                if(bccno[x.v]!=bcc_cnt){bcc[bcc_cnt].push_back(x.v);bccno[x.v]=bcc_cnt;}
                if(x.u==u&&x.v==v) break;
            }
           }
         }
         else if(pre[v]<pre[u]&&v!=fa){
            S.push(e);
            lowu=min(lowu,pre[v]);
         }
     }
    if(fa<0&&child==1) iscut[u]=0;
    return lowu;
}

void find_bcc(int n){
  memset(pre,0,sizeof pre);
  memset(iscut,0,sizeof iscut);
  memset(bccno,0,sizeof bccno);
  dfs_clock=bcc_cnt=0;
  for(int i=1;i<=n;i++)
    if(!pre[i]) dfs(i,-1);
}

int main(){
   int kase=0,n,m;
   while(scanf("%d%d",&n,&m)==2&&n){
    for(int i=0;i<m;i++){
       int u,v;
       scanf("%d%d",&u,&v);
       A[u][v]=A[v][u]=1;
    }

    for(int i=1;i<=n;i++)
    for(int j=i+1;j<=n;j++){
        if(!A[i][j]){/*cout<<i<<"-"<<j<<"\n";*/add(i,j);add(j,i);}
    }

    //for(int i=1;i<=n;i++)
    //{
      //  cout<<i<<":";
        //for(int j=head[i];j;j=edge[j].next)
          //  cout<<edge[j].to<<" ";
        //printf("\n");
    //}

    find_bcc(n);
    for(int i=1;i<=bcc_cnt;i++){
        memset(color,0,sizeof color);
        for(int j=0;j<bcc[i].size();j++){
           bccno[bcc[i][j]]=i;
        }
        int u=bcc[i][0];
        color[u]=1;
    if(!bipartite(u,i)) for(int j=0;j<bcc[i].size();j++) odd[bcc[i][j]]=1;
    }

    int ans=n;
    for(int i=1;i<=n;i++)
        if(odd[i]) ans--;

        printf("%d",ans);
   }
  return 0;
}

猜你喜欢

转载自www.cnblogs.com/rainyskywx/p/10842082.html
今日推荐