円卓の[Cerc2005]騎士

タイトル説明

多くの場合、イベントを議論するために、ラウンドテーブルを開催したn個の騎士があります。各ラウンドテーブルには、少なくとも3人の騎士に参加する、と円卓の騎士の相互の憎悪は、隣接する位置に座ることができません。不一致が発生した場合、我々は承認として、それに対してできるだけ多くのを防止するために、1以上の奇数でなければならない会議に出席するために挙手ので、騎士の番号が必要です。騎士の後にお互いを憎むことを知って、あなたのタスクは、騎士が任意の会議に参加することはできませんどのように多く数えることです。

入力形式

最初の二つの整数のn行及びmは(1 <= N <= 1000、1 <= M <= 10 ^ 6):データの複数のセットを備え、各データ形式は以下の通りです。次のm行二つの整数K1およびK2(1 <= K1、K2 <= n)を含み、K1およびK2はそれぞれナイト憎悪を表します。端部が入力を標識N = M = 0

出力フォーマット

各試験のために、出力行の整数、どの会議で騎士の数を参加することはできません。


我々は明らかにキャバリアーズが会議に来て関係を嫌いではありませんでしたようにする必要があります。だから我々は、元のマップを構築するために開始することができます。

その後、我々はいくつかの騎士が会議に出席するならば、これらの騎士が奇数のリングを構成するであろうことがわかりました。それはリングであっても、その後、リング上の点は確かにも、隣接する二つの側面が座るためにはポイントがないことを意味し、選挙を終えることができないので。隣人は人を憎むことができない、と私たちはマップを構築しているので、何点でも側には、憎しみの騎士ではありません。だから、リング上の騎士でも会議に出席することはできません。

建設は、相補グラフであるので、我々は簡単に絵を描くのデータを構築する2点で同じ騎士に同じ会議に出席することができないではない、見え隠れすることができます。

あなたが同じ会議に参加することができた場合、2つの点が同じ奇数リング内でなければならないので、実際には、その理由は、非常に簡単です。そして、彼らは二重の内の同じ時点ではない、それは同じ奇数のリングでは不可能です。

二点定理は、あります:

二点内部奇数環場合、2つの点全体のすべての点は、特定の奇数の環内になければなりません。

証明は以下の通りである:我々は、Uを2点をとり、奇数の二重リングの時点でVは、次に、点Wが存在しなければならないことは、U、V、Wは、単純なリングを構成するように、奇数の外側リングです。そうU->パス上の点の数WパスおよびV-> wは、それが偶数又は奇数であってもよいです。しかし、どんなに奇数またはuが奇数リングによって到達する2つのパスが存在するVためにも、元の奇数リングと別の奇数の環を形成することができる、2つのパスポイントの奇数、どちらかの点の偶数番号のいずれかであり、構成することができます別の奇妙なリング。


だから、私たちのアルゴリズムアウト:

まず、私たちは二重のTarjanとポイントマップ、および各デュアルポイント列挙のための通信コンポーネントの接続されているすべてのコンポーネントを見つけることができ、我々はそれが奇数環であるかどうかを判断します。奇数リング判定方法は以下のとおりです。

二部グラフの定義によれば、二部グラフは、奇数環ではありません。だから我々はこの点は二重の二部グラフ、そうでない場合はそれ奇数リングであるかどうかを判断することができます。最後の答えは、ポイントの数を引い奇数リングとダブルポイントです。

時間計算量はO(N ^ 2 + M)、N ^ 2は、図のために構築されます。他の時間計算量はO(N + M)です。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#define maxn 1001
#define maxm 1000001
using namespace std;
  
vector<int> to[maxn],dcc[maxn];
bool g[maxn][maxn],able[maxn];//able表示能够参加会议
int dfn[maxn],low[maxn],tot;
int stack[maxn],top;
int col[maxn],cnt;
int vis[maxn];
int n,m,ans;
  
inline int read(){
    register int x(0),f(1); register char c(getchar());
    while(c<'0'||'9'<c){ if(c=='-') f=-1; c=getchar(); }
    while('0'<=c&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
  
inline void init(){
    for(register int i=1;i<=n;i++) for(register int j=1;j<=n;j++) g[i][j]=false;
    for(register int i=1;i<=n;i++) dfn[i]=low[i]=vis[i]=col[i]=able[i]=0;
    for(register int i=1;i<=n;i++) to[i].clear(),dcc[i].clear();
    tot=cnt=0,ans=0;
}
  
void tarjan(int u){
    dfn[u]=low[u]=++tot;
    stack[++top]=u;
    for(register int i=0;i<to[u].size();i++){
        int v=to[u][i];
        if(!dfn[v]){
            tarjan(v),low[u]=min(low[u],low[v]);
            if(dfn[u]<=low[v]){
                int w; cnt++;
                do{ w=stack[top--],dcc[cnt].push_back(w); }while(w!=v);
                dcc[cnt].push_back(u);
            }
        }else low[u]=min(low[u],dfn[v]);
    }
}
  
bool check(int u,int c,const int &id){
    vis[u]=c;
    for(register int i=0;i<to[u].size();i++){
        int v=to[u][i];
        if(col[v]!=id) continue;
        if(!vis[v]){
            if(check(v,3-c,id)) return true;
        }else if(vis[v]==c) return true;
    }
    return false;
}
  
int main(){
    while(scanf("%d %d",&n,&m)==2&&n&&m){
        init();
        for(register int i=1;i<=m;i++){
            int u=read(),v=read();
            g[u][v]=g[v][u]=true;
        }
        for(register int i=1;i<=n;i++){
            for(register int j=1;j<i;j++) if(!g[i][j]){
                to[i].push_back(j),to[j].push_back(i);
            }
        }
  
        for(register int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
        for(register int i=1;i<=cnt;i++){
            for(register int j=0;j<dcc[i].size();j++){
                col[dcc[i][j]]=i,vis[dcc[i][j]]=0;
            }
            if(check(dcc[i][0],1,i)){
                for(register int j=0;j<dcc[i].size();j++) able[dcc[i][j]]=true;
            }
        }
        for(register int i=1;i<=n;i++) if(!able[i]) ans++;
        printf("%d\n",ans);
    }
    return 0;
}

おすすめ

転載: www.cnblogs.com/akura/p/10958431.html