zoukankan      html  css  js  c++  java
  • [SP2878]KNIGHTS

    [SP2878]KNIGHTS - Knights of the Round Table

    一.前言

    ​ 脑阔完全转不动……都不知道要干些什么,题目链接

    二.思路

    ​ 首先它是圆桌骑士开会,所以他们开会的样子是一个环,并且避免平票需要只有奇数个人(不能一人开会)。将仇恨关系建图不好做,于是建立补图,将可以坐在一起的骑士连边,这样形成了一个可能的座位形状?然后去里面找尽可能多的奇环。输出不被任何一个奇环包括的骑士。

    ​ 然后到这里我们会有一个结论如下:

    对于一个点双连通分量,里面若是有一个奇环,那么该点双连通分量里面所有的点都可以被包括在奇环之中

    虽然我也不知道为什么会出现这个结论(好像写边双也可)就象征性的推导一下:

    首先点双连通分量由一些环组成

    假设在点双连通分量之中有环 a ,若 a 是奇环,那么 a 中所有点都可以开会,不影响答案

    若 a 是偶环,那么将它和点双中的奇环链接成一个大奇环就可以了。

    我也不知道是不是对的hhh,感性理解一下……同样我也不好解释奇环和点双的关系,硬要说的话,就是 tarjan 缩点之后不会有环,换而言之所有的环都在点双连通分量之中,由于这个结论很方便,就这么写了

    ​ 现在需要判断一个点双里面有没有奇环。对于一个没有奇环的图来说,他一定是一个二分图(有了奇环就构不成二分图),转化为判断一个点双是不是二分图,使用二分图黑白染色,即将一个点周围的点全部染上相异的颜色,有冲突就不是二分图,也就是有奇环。

    ​ 这样写就可以了。

    三.CODE

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<fstream>
    #include<cmath>
    #include<cstring>
    using namespace std;
    int read(){
    	char ch=getchar();
    	int res=0,f=1;
    	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    	for(;ch>='0'&&ch<='9';ch=getchar())res=res*10+(ch-'0');
    	return res*f;
    }
    const int MAXN=1e6+5;
    int n,m;
    bool hate[1005][1005];
    int head[1005],ne[MAXN],to[MAXN],tot;
    void add(int x,int y){
    	to[++tot]=y,ne[tot]=head[x],head[x]=tot;
    }
    void build(){
    	for(int i=1;i<=n;++i)
    		for(int j=i+1;j<=n;++j)
    			if(!hate[i][j])add(i,j),add(j,i);
    }
    int dfn[1005],low[1005],date;
    int st[1005],top,rs[1005];
    bool vis[1005],fl,wdnmd[1005];
    int ans;
    bool check(int x,int fa){
    	if(fl)return 1;
    	for(int i=head[x];i;i=ne[i]){
    		int v=to[i];
    		if(v==fa||!vis[v])continue;
    		if(rs[v]==-1){
    			rs[v]=rs[x]^1;//染色
    			check(v,x);
    			if(fl)return 1;
    		}
    		else if(rs[v]!=(rs[x]^1))return (fl=1);//冲突
    	}
    	return 0;
    }
    void dfs(int x,int fa){//tarjan
    	dfn[x]=low[x]=++date;
    	st[++top]=x;
    	for(int i=head[x];i;i=ne[i]){
    		int v=to[i];
    		if(!dfn[v]){
    			dfs(v,x);
    			low[x]=min(low[x],low[v]);
    			if(low[v]>=dfn[x]){//出现点双
    				int count=1;
    				fl=0;
    				memset(vis,0,sizeof(vis));
    				memset(rs,-1,sizeof(rs));
    				vis[x]=1;//将当前点双之中的全部打上标记
    				rs[x]=0;
    				while(st[top]!=v)vis[st[top--]]=1,count++;
    				vis[v]=1,top--,count++;
    				if(count>=3&&check(x,x))for(int i=1;i<=n;++i)if(vis[i])wdnmd[i]=1;
                    //二分图染色
    			}
    		}
    		else low[x]=min(low[x],dfn[v]);
    	}
    }
    void clean(){
    	memset(hate,0,sizeof(hate));
    	memset(head,0,sizeof(head));
    	tot=0;ans=0;
    	memset(dfn,0,sizeof(dfn));
    	memset(low,0,sizeof(low));
    	memset(wdnmd,0,sizeof(wdnmd));
    }
    int main(){
    	while(1){
    		n=read();m=read();
    		if(n==m&&n==0)break;
    		clean();//多测清空
    		for(int i=1,x,y;i<=m;++i){
    			x=read();y=read();
    			hate[x][y]=hate[y][x]=1;
    		}
    		build();//建立补图
    		for(int i=1;i<=n;++i)if(!dfn[i]){//可能会不连通,找点双
    			memset(dfn,0,sizeof(dfn));
    			memset(low,0,sizeof(low));
    			date=top=0;
    			dfs(i,i);
    		}
    		for(int i=1;i<=n;++i)ans+=(wdnmd[i]==0);//加上不被点双标记的
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    第一堂课20210302
    第一堂课20210301
    第一堂课20210301
    实验2-3-1 求1到100的和 (10分)
    实验2-3-2 求N分之一序列前N项和 (15分)
    实验2-3-3 求奇数分之一序列前N项和 (15分)
    实验2-3-4 求简单交错序列前N项和 (15分)
    实验2-3-5 输出华氏-摄氏温度转换表 (15分)
    实验2-3-6 求交错序列前N项和 (15分)
    实验2-3-7 求平方与倒数序列的部分和 (15分)
  • 原文地址:https://www.cnblogs.com/clockwhite/p/13531268.html
Copyright © 2011-2022 走看看