• 【POJ2942】Knights of the Round Table(二分图 点双联通分量)


    题目链接

    大意

    给定(N)个点与(M)个关系,每个关系表示某两个点间没有直接的边相连,求不在所有奇环上的点的个数。

    ((1le Nle 1e3,1le Mle 1e6))

    思路

    考虑到(N)比较小的缘故,我们不妨暴力连边。
    对于现在得到的一个图,我们需要找出所有在奇环上的点。
    考虑使用点双联通分量对图进行缩点,对于每个点双分量,暴力的去判断它是否是二分图。

    • ①如果是二分图,那么显然无奇环,对该点双上的点不做修改。
    • ②如果不是二分图,那么对于这个点双联通分量,一定会有一个奇环, 而通过这个奇环一定可以使其他所有点在一个奇环上。

    对于②情况的证明如下:
    我们设有该点双分量上的某一段,使得两个端点都在该奇环上,显然每个点都会在至少一个这样的段上。
    则连边关系分别是从该奇环上的某个奇段或偶段出发与图上的这一段形成环,则可以根据这一段的奇偶性与其对应的形成一个奇环。

    (注:①情况不做修改是为了不覆盖②情况下处理出的答案,点双会重点)
    (注:不用边双联通分量主要是为了让每个联通分量内部不存在两环只共一点的8字形)

    代码

    略丑,见谅

    #include<map>
    #include<queue>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int MAXN=1005;
    const int MAXM=1000005;
    int N,M,Cnt,Root=1;
    int dfn[MAXN],low[MAXN];
    vector<int>P[MAXN],T[MAXN];
    int Anscnt,stac[MAXN],len;
    vector<int>HP[MAXM];
    void DFS(int u,int fad){
    	stac[++len]=u;
    	low[u]=dfn[u]=++Cnt;
    	int size=P[u].size();
    	for(int i=0;i<size;i++){
    		int v=P[u][i];
    		if(dfn[v]){
    			if(T[u][i]==fad)continue;
    			low[u]=min(low[u],dfn[v]);
    			continue;
    		}
    		DFS(v,T[u][i]);
    		low[u]=min(low[u],low[v]);
    		if(low[v]>=dfn[u]){
    			HP[++Anscnt].push_back(u);
    			while(1){
    				int x=stac[len--];
    				HP[Anscnt].push_back(x);
    				if(x==v)break;
    			}
    		}
    	}
    }
    void Add(int x,int y,int id){
    	P[x].push_back(y);
    	T[x].push_back(id);
    	P[y].push_back(x);
    	T[y].push_back(id);
    }
    int Vis[MAXN],Col[MAXN];
    int KM[MAXN][MAXN];
    int ans[MAXN],Ans;
    int Check(int u){
    	Vis[u]=1;
    	int size=P[u].size();
    	for(int i=0;i<size;i++){
    		int v=P[u][i];
    		if(Vis[v]==-1)continue;
    		if(Vis[v]){
    			if(Col[v]==Col[u])
    				return 1;
    			continue;
    		}
    		Col[v]=(Col[u]+1)%2;
    		if(Check(v))return 1;
    	}
    	return 0;
    }
    void Init(){
    	memset(Vis,-1,sizeof(Vis));
    	memset(Col,0,sizeof(Col));
    	for(int i=1;i<=Anscnt;i++){
    		int size=HP[i].size();
    		for(int j=0;j<size;j++)
    			Vis[HP[i][j]]=0;
    		if(size){
    			if(Check(HP[i][0])){
    				for(int j=0;j<size;j++)
    					ans[HP[i][j]]=1;
    			}
    		}
    		for(int j=0;j<size;j++)
    			Vis[HP[i][j]]=-1,Col[HP[i][j]]=0;
    	}
    }
    void clear(){
    	Cnt=len=0;
    	for(int i=1;i<=N;i++){
    		ans[i]=0;Col[i]=0;
    		dfn[i]=low[i]=stac[i]=0;
    		P[i].clear(),T[i].clear();
    		for(int j=1;j<=N;j++)
    			KM[i][j]=0;
    	}
    	for(int i=1;i<=Anscnt;i++)HP[i].clear();
    	N=M=Anscnt=Ans=0;
    }
    int main(){
    	while(~scanf("%d%d",&N,&M)&&N&&M){
    		for(int i=1,x,y;i<=M;i++){
    			scanf("%d%d",&x,&y);
    			KM[x][y]=KM[y][x]=1;
    		}M=0;
    		for(int i=1;i<=N;i++)
    			for(int j=i+1;j<=N;j++)
    				if(!KM[i][j])Add(i,j,++M);
    		for(int i=1;i<=N;i++)
    			if(!dfn[i])DFS(i,0);
    		Init();
    		for(int i=1;i<=N;i++)
    			if(!ans[i])Ans++;
    		printf("%d
    ",Ans);
    		clear();
    	}
    	return 0;
    }
    /*
    5 5
    1 4
    1 5
    2 5
    3 4
    4 5
    5 5
    1 4
    1 5
    2 5
    3 4
    4 5
    5 5
    1 4
    1 5
    2 5
    3 4
    4 5
    0 0
    */
    
  • 相关阅读:
    求一个字符串中连续出现次数最多的子串
    LintCode: Longest Common Substring
    LintCode: O(1) Check Power of 2
    LintCode: Fizz Buzz
    LintCode: 3 Sum
    LintCode: Two Sum
    LintCode: Sort Colors
    LintCode: Median of two Sorted Arrays
    LintCode: Search A 2d Matrix
    Lintcode: Sqrt(X)
  • 原文地址:https://www.cnblogs.com/ftotl/p/11769460.html
Copyright © 2020-2023  润新知