• 【POJ2942】Knights of the Round Table


    题目

    题目链接:http://poj.org/problem?id=2942

    \(n\) 个骑士经常举行圆桌会议,商讨大事。每次圆桌会议至少有 3 个骑士参加,且相互憎恨的骑士不能坐在圆桌的相邻位置。如果发生意见分歧,则需要举手表决,因此参加会议的骑士数目必须是大于 1 的奇数,以防止赞同和反对票一样多。知道那些骑士相互憎恨之后,你的任务是统计有多少骑士不可能参加任何一个会议。

    思路:

    首先如果骑士 \(i\)\(j\) 不互相憎恨,那么就将 \(i,j\) 连边。此时一个会议选择的骑士应当是一个奇环内的所有点。

    那么对于任意两个骑士 \(x,y\),如果 \(x\)\(y\) 不在一个点双连通分量内,那么 \(x\)\(y\) 不可能同时出席一个会议。因为 \(x\)\(y\) 不可能处于同一个环中。

    Tarjan 求出每一个点双连通分量,容易发现,如果这个点双连通分量中如果有奇环,那么整个点双连通分量的点都会被至少一个奇环包含。

    那么只需判断每个连通分量是否包含奇环即可。如果一个点双连通分量不含奇环,那么它必然是一个二分图。果断二分图染色。

    时间复杂度 \(O(n^2)\)

    代码

    #include <stack>
    #include <vector>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int N=1010;
    int n,m,ans,cnt,tot,col[N],head[N],dfn[N],low[N];
    bool hate[N][N],flag[N],vis[N];
    stack<int> st;
    vector<int> dcc[N];
    
    struct edge
    {
    	int next,to;
    }e[N*N*2];
    
    void add(int from,int to)
    {
    	e[++tot].to=to;
    	e[tot].next=head[from];
    	head[from]=tot;
    }
    
    bool dfs(int x)
    {
    	for (int i=head[x];~i;i=e[i].next)
    	{
    		int v=e[i].to;
    		if (flag[v])
    		{
    			if (col[v]==col[x]) return 0;
    			if (!col[v])
    			{
    				col[v]=col[x]^1;
    				if (!dfs(v)) return 0;
    			}
    		}
    	}
    	return 1;
    }
    
    void tarjan(int x)
    {
    	dfn[x]=low[x]=++tot;
    	st.push(x);
    	for (int i=head[x];~i;i=e[i].next)
    	{
    		int v=e[i].to;
    		if (!dfn[v])
    		{
    			tarjan(v);
    			low[x]=min(low[x],low[v]);
    			if (low[v]>=dfn[x])
    			{
    				int y=0;
    				cnt++;
    				do {
    					y=st.top();
    					st.pop();
    					dcc[cnt].push_back(y);
    				} while (y!=v);
    				dcc[cnt].push_back(x);
    			}
    		}
    		else low[x]=min(low[x],dfn[v]);
    	}
    }
    
    int main()
    {
    	while (scanf("%d%d",&n,&m)>0 && n)
    	{
    		memset(vis,0,sizeof(vis));
    		memset(dcc,0,sizeof(dcc));
    		memset(dfn,0,sizeof(dfn));
    		memset(hate,0,sizeof(hate));
    		memset(head,-1,sizeof(head));
    		tot=ans=cnt=0;
    		for (int i=1,x,y;i<=m;i++)
    		{
    			scanf("%d%d",&x,&y);
    			hate[x][y]=hate[y][x]=1;
    		}
    		for (int i=1;i<=n;i++)
    			for (int j=1;j<=n;j++)
    				if (!hate[i][j] && i!=j)
    					add(i,j),add(j,i);
    		tot=0;
    		for (int i=1;i<=n;i++)
    			if (!vis[i])
    			{
    				while (st.size()) st.pop();
    				tarjan(i);	
    			}
    		for (int i=1,k;i<=cnt;i++)
    		{
    			for (int j=0;j<dcc[i].size();j++)
    				flag[dcc[i][j]]=1;
    			col[dcc[i][0]]=114514;
    			k=!dfs(dcc[i][0]);
    			for (int j=0;j<dcc[i].size();j++)
    			{
    				vis[dcc[i][j]]|=k;
    				flag[dcc[i][j]]=col[dcc[i][j]]=0;
    			}
    		}
    		for (int i=1;i<=n;i++)
    			ans+=vis[i];
    		printf("%d\n",n-ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    linux进程间通信--信号量
    linux进程间通信--信号通信
    linux进程间通信--管道通信
    探究守护进程及其错误日志处理
    探究wait与waitpid之间的那些事
    探究一下strtok的用法
    文件IO与标准IO探究及总结
    Linux 库的制作--动态库与静态库
    python基础使用
    linux正则表达式使用
  • 原文地址:https://www.cnblogs.com/stoorz/p/13088762.html
Copyright © 2020-2023  润新知