zoukankan      html  css  js  c++  java
  • POJ2942 Knights of the Round Table

    POJ2942 Knights of the Round Table

    开始看错题了,“某些骑士无法出席所有会议”并不是无法出席某个会议,

    我们尝试建图,按照题目给的条件,我们可以将相互仇恨的骑士之间建边。那么骑士可以坐在一起的条件就是他们之间没有边。所以我们建出这个图的补图会使解题更加方便。 然后我们可以发现一些显然的事情,比如度数<=1的点一定参加不了。一条链上的点也一定参加不了。 那么我们可以想到,能够参加会议的骑士一定在一个环里面。但在一个环里面的骑士并不一定可以参加会议。所以我们要做的就是思考如何对每一组双连通分量进行处理,其实很显然,只有处在一个奇圈内的骑士才可以参加。那么我们如何判断是否有奇圈存在呢? 给出两个定理: 1、如果一个双连通分量内的某些顶点在一个奇圈中(即双连通分量含有奇圈),那么这个双连通分量的其他顶点也在某个奇圈中 2、如果一个双连通分量含有奇圈,则他必定不是一个二分图。反过来也成立,这是一个充要条件。 证明好像挺简单的…… 所以,我们的任务就变成了求一个图是否为二分图。而求一个图是否为二分图的方法就是交叉染色法(说白了就是让每条边两端点颜色不同,随便dfs一下就好了)。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<vector>
    #define int long long
    #define MAXN 5010
    #define ma(x) memset(x,0,sizeof(x))
    using namespace std;
    struct edge
    {
    	int u,v,nxt;
    	#define u(x)  ed[x].u
    	#define v(x)  ed[x].v
    	#define n(x)  ed[x].nxt
    }ed[MAXN*100];
    int first[MAXN],num_e;
    #define f(x) first[x]
    bool map[1010][1010];
    int n,m;
    
    int dfn[MAXN],low[MAXN],num,root;
    int stack[MAXN],top,cnt;
    bool cut[MAXN];int c[MAXN];
    vector<int> dcc[MAXN];
    vector<int> inc[MAXN];
    void tarjan(int x)
    {
    	dfn[x]=low[x]=++num;
    	stack[++top]=x;int flag=0;
    	if(x==root&&!f(x)){dcc[++cnt].push_back(x);return;}
    	for(int i=f(x);i;i=n(i))
    	if(!dfn[v(i)])
    	{
    		tarjan(v(i)),low[x]=min(low[x],low[v(i)]);
    		if(low[v(i)]>=dfn[x])
    		{
    			flag++;if(x!=root||flag>1)cut[x]=1;
    			int z;++cnt;
    			do{dcc[cnt].push_back(z=stack[top--]);}while(z!=v(i));
    			dcc[cnt].push_back(x);
    		}
    	}
    	else low[x]=min(low[x],dfn[v(i)]);
    }
    bool pd(int x,int dc)
    {
    	if(c[x]&&c[x]!=dc)return 0;
    	if(c[x]==dc)return 1;
    	for(int j=0;j<inc[x].size();j++)
    	if(inc[x][j]==dc)return 1;
    	return 0;
    }
    int v[MAXN];bool is[MAXN];
    bool dfs(int x,int co,int dc,int fa)
    {
    	if(v[x]&&v[x]!=co){is[dc]=1;return 0;}
    	v[x]=co;
    	for(int i=f(x);i;i=n(i))
    	if(v(i)!=fa)
    	if(pd(v(i),dc))
    		if(v[v(i)]&&v[v(i)]!=co^1){is[dc]=1;return 0;}
    		else if(!v[v(i)] && !dfs(v(i),co^1,dc,x)){return 0;}
    	return 1;
    }
    void init()
    {	
    	ma(ed);ma(first);ma(dfn);ma(low);ma(dcc);ma(c);ma(cut);ma(map);ma(is);ma(inc);
    	num_e=num=cnt=top=0;
    }
    int ad(int x)
    {
    	for(int j=0;j<inc[x].size();j++)
    	if(is[inc[x][j]])return 0;
    	return 1;
    }
    inline void add(int u,int v);
    inline void add2(int u,int v);
    signed main()
    {
    //	freopen("in.txt","r",stdin);
    
    	while(scanf("%lld%lld",&n,&m))	
    	{
    		if(!n&&!m)return 0;
    		init();
    		int a,b;
    		for(int i=1;i<=m;i++)
    		{
    			scanf("%lld%lld",&a,&b);
    			map[a][b]=map[b][a]=1;
    		}
    		for(int i=1;i<=n;i++)
    		for(int j=1;j<i;j++)
    		if(!map[i][j])add(i,j),add(j,i);
    		for(int i=1;i<=n;i++)
    		if(!dfn[i]){root=i;tarjan(i);}
    		for(int i=1;i<=cnt;i++)
    		for(int j=0;j<dcc[i].size();j++)
    		{	
    			int x=dcc[i][j];
    			if(!cut[x]) c[x]=i;
    			else inc[x].push_back(i);
    		}
    		int ans=0;
    		for(int i=1;i<=cnt;i++)
    		{
    			memset(v,0,sizeof(v));
    			dfs(dcc[i][0],2,i,0);
    		}
    		for(int i=1;i<=n;i++)
    		if(!cut[i])ans+=is[c[i]]^1;
    		else ans+=ad(i);
    		printf("%lld
    ",ans);
    	}
    }
    inline void add(int u,int v)
    {
    	++num_e;
    	u(num_e)=u;
    	v(num_e)=v;
    	n(num_e)=f(u);
    	f(u)=num_e;
    }
    
  • 相关阅读:
    ubuntu应用商店打不开怎么办
    java线程池系列(1)-ThreadPoolExecutor实现原理
    java并发之SynchronousQueue实现原理
    java并发等待条件的实现原理(Condition)
    轻松掌握java读写锁(ReentrantReadWriteLock)的实现原理
    Java NIO 内存映射文件
    Java NIO Path接口和Files类配合操作文件
    Java 序列化 序列化与单例模式 [ 转载 ]
    Java 序列化 JDK序列化总结
    Java 泛型 Java使用泛型的意义
  • 原文地址:https://www.cnblogs.com/Al-Ca/p/11186746.html
Copyright © 2011-2022 走看看