• [POJ2942]:Knights of the Round Table(塔尖+二分图染色法)


    题目传送门


    题目描述

    亚瑟王要在圆桌上召开骑士会议,为了不引发骑士之间的冲突,并且能够让会议的议题有令人满意的结果,每次开会前都必须对出席会议的骑士有如下要求:

      1、相互憎恨的两个骑士不能坐在直接相邻的2个位置。

      2、出席会议的骑士数必须是奇数,这是为了让投票表决议题时都能有结果。 如果出现有某些骑士无法出席所有会议(例如这个骑士憎恨所有的其他骑士),则亚瑟王为了世界和平会强制把他剔除出骑士团。       

    现在给定准备去开会的骑士数n,再给出m对憎恨对(表示某2个骑士之间使互相憎恨的),问亚瑟王至少要剔除多少个骑士才能顺利召开会议? 

    注意:

      1、所给出的憎恨关系一定是双向的,不存在单向憎恨关系。

      2、由于是圆桌会议,则每个出席的骑士身边必定刚好有2个骑士。即每个骑士的座位两边都必定各有一个骑士。

      3、一个骑士无法开会,就是说至少有3个骑士才可能开会。


    输入格式

    输入包含多组测试。 每种情况都以包含两个整数1≤n≤10001≤m≤1000000的整数行开始。 数字n是骑士的数量。 接下来的m行描述哪个骑士憎恨哪个骑士。 m行中的每一行包含两个整数k1k2,这意味着骑士数k1和骑士数k2彼此讨厌(数字k1k21n之间)。
    输入n=m=0时终止。


    输出格式

    对于每组测试,输出一个整数,表示必须被驱逐的骑士数量。


    样例

    样例输入:

    5 5
    1 4
    1 5
    2 5
    3 4
    4 5
    0 0

    样例输出:

    2


    题解

    些许有些复杂,思维量较大。

    注意亚瑟王会召开多次会议,可以参加其中任意一次会议的骑士就可以被保留。

    考虑建出原图的补图,其定义为:原来相连的两个点现在不相连,原来不相连的两个点现在相连。

    这样的话两个其实可以坐在一起的条件即为他们之间有连边,方便处理。

    那么,如果一个骑士可以参加会议,当且仅当他在一个奇环里。

    给出两个定理:

      1、如果一个双连通分量内的某些顶点在一个奇圈中(即双连通分量含有奇圈),那么这个双连通分量的其他顶点也在某个奇圈中。

      2、如果一个双连通分量含有奇圈,则他必定不是一个二分图。反过来也成立,这是一个充要条件。

    显然利用塔尖求出每一个v-DCC,然后判断这个v-DCC是不是奇环即可。

    这时就用到了交叉染色法,dfs时每一条边都和上一条边反色,当发现两条相邻的边同色时即为奇环。

    特别注意:此题不能用万能头文件,否则会CE!!!

    那些被CE打倒的大佬:

    不过结局总会是好的:


    代码时刻

    #include<cstdio>
    #include<iostream>
    #include<vector>
    #include<cstring>//不要万能头!!!
    using namespace std;
    struct rec
    {
    	int nxt;
    	int to;
    }e[1000001];
    int head[1001],cnt;
    int dfn[1001],low[1001],sta[1001],tot,top;
    bool par[1001],vis[1001];
    int col[1001];
    bool Map[1001][1001];
    vector<int> dcc;
    void pre_work()//多测不清空,爆零两行泪TAT……
    {
    	cnt=0;
    	tot=0;
    	top=0;
    	memset(head,0,sizeof(head));
    	memset(dfn,0,sizeof(dfn));
    	memset(low,0,sizeof(low));
    	memset(sta,0,sizeof(sta));
    	memset(vis,0,sizeof(vis));
    	memset(Map,0,sizeof(Map));
    }	
    void add(int x,int y)//建边
    {
    	e[++cnt].nxt=head[x];
    	e[cnt].to=y;
    	head[x]=cnt;
    }
    bool dfs(int x,int color)//交叉染色法判奇环
    {
    	col[x]=color;
    	for(int i=head[x];i;i=e[i].nxt)
    	{
    		if(!par[e[i].to])continue;
    		if(col[e[i].to]==color)return 1;
    		if(!col[e[i].to]&&dfs(e[i].to,-color))return 1;
    	}
    	return 0;
    }
    void color_solve()//将v-DCC转入数组,方便处理
    {
    	memset(par,0,sizeof(par));
    	memset(col,0,sizeof(col));
    	for(int i=0;i<dcc.size();i++)
    		par[dcc[i]]=1;
    	if(dfs(dcc[0],1))
    		for(int i=0;i<dcc.size();i++)vis[dcc[i]]=1;
    }
    void tarjan(int x)//塔尖
    {
    	dfn[x]=low[x]=++tot;
    	sta[++top]=x;
    	for(int i=head[x];i;i=e[i].nxt)
    	{
    		if(!dfn[e[i].to])
    		{
    			tarjan(e[i].to);
    			low[x]=min(low[x],low[e[i].to]);
    			if(dfn[x]<=low[e[i].to])//发现v-DCC
    			{
    				int y;
    				dcc.clear();
    				do
    				{
    					y=sta[top--];
    					dcc.push_back(y);
    				}while(e[i].to!=y);
    				dcc.push_back(x);
    				color_solve();
    			}
    		}
    		else low[x]=min(low[x],dfn[e[i].to]);
    	}
    }
    int main()
    {
    	while(1)
    	{
    		int n,m;
    		scanf("%d%d",&n,&m);
    		if(!n&&!m)break;
    		pre_work();
    		for(int i=1;i<=m;i++)
    		{
    			int x,y;
    			scanf("%d%d",&x,&y);
    			Map[x][y]=Map[y][x]=1;//临接矩阵存图
    		}
    		for(int i=1;i<=n;i++)
    			for(int j=i+1;j<=n;j++)
    				if(!Map[i][j]){add(i,j);add(j,i);}//链式前项星建反图
    		for(int i=1;i<=n;i++)
    			if(!dfn[i])tarjan(i);
    		int ans=0;
    		for(int i=1;i<=n;i++)//统计可以参加的骑士
    			if(vis[i])ans++;
    		printf("%d
    ",n-ans);//用总骑士数减去
    	}
    	return 0;
    }
    

    rp++

  • 相关阅读:
    python面向对象-3类的静态方法和类方法
    python面向对象-2深入类的属性
    python面向对象-1方法、构造函数
    python小练习--函数调用函数,让对象具有能动性
    python小练习--属性
    python面向对象开发的自我理解
    python入门前的准备
    python类的继承-1
    有关孔隙比的基本概念和计算公式
    一维固结试验过程
  • 原文地址:https://www.cnblogs.com/wzc521/p/11183707.html
Copyright © 2020-2023  润新知