码迷,mamicode.com
首页 > 其他好文 > 详细

10.1 国庆节给祖国母亲献礼 NOIP 模拟题 第二题题解

时间:2017-10-01 19:27:39      阅读:186      评论:0      收藏:0      [点我收藏+]

标签:href   简单   时间   lin   ++   pac   除了   UI   rip   

2503: 相框

Time Limit: 3 Sec  Memory Limit: 128 MB
Submit: 108  Solved: 52
[Submit][Status][Discuss]

Description

       P大的基础电路实验课是一个无聊至极的课。每次实验,T君总是提前完成,管理员却不让T君离开,T君只能干坐在那儿无所事事。
       先说说这个实验课,无非就是把几根导线和某些元器件(电阻、电容、电感等)用焊锡焊接起来。
       为了打发时间,T君每次实验做完后都在焊接一些诡异的东西,这就是他的杰作:
 

 

       T君不满足于焊接奇形怪状的作品,强烈的破坏欲驱使他拆掉这个作品,然后将之焊接成规整的形状。这会儿,T君正要把这个怪物改造成一个环形,当作自己的相框,步骤如下:

 

 

 

 

 

 

T君约定了两种操作:

1.      烧熔一个焊点:使得连接在焊点上的某些导线相分离或保持相连(可以理解为:把焊点上的导线划分为若干个类,相同类中的导线相连,不同类之间的导线相离)

2.      将两根导线的自由端(即未与任何导线相连的一端)焊接起来。

 

例如上面的步骤中,先将A点烧熔,使得导线1与导线2、4点分离;再将D点烧熔,使得4、5与3、7相离;再烧熔E,使7与6、8相离;最后将1、7相连。

T君想用最少的操作来将原有的作品改造成为相框(要用上所有的导线)。

 

Input

输入文件的第一行共有两个整数nm — 分别表示原有的作品的焊点和导线的数量 (0 ≤ n ≤ 1 000, 2 ≤ m ≤ 50 000)。焊点的标号为1~n。 接下来的m行每行共有两个整数 — 导线两端所连接的两个焊点的标号,若不与任何焊点相连,则将这一端标号为0。

原有的作品可能不是连通的。

某些焊点可能只有一根导线与之相连,在该导线的这一端与其他导线相连之前,这些焊点不允许被烧熔。

某些焊点甚至没有任何导线与之相连,由于T君只关心导线,因此这些焊点可以不被考虑。

 

 

Output

 

       输出文件只包含一个整数——表示T君需要将原有的作品改造成相框的最少步数。

Sample Input

6 8
1 2
1 3
3 4
1 4
4 6
5 6
4 5
1 5

Sample Output

4

HINT

 

30%的数据中n≤10;


100%的数据中n≤1000。

 

  这道题当时一看不可做,除了样例和n=0以外直接输出的n才20分,然而,原子核输出了m骗到了50分%%%orz。
  事实证明这道题得分除了骗来的分为两种,一种是基本想通了所有细节,结果对于自环处理不当或其他细节险些AC的和如我一般接近爆零的。差距啊~~  
    写这篇题解之前我不会的预备知识:

    欧拉通路:从图中一个点出发不重复地遍历所有边的路径(可以停在另一个点)
    欧拉回路:从图中一个点出发不重复地遍历所有边的回路(必须回到出发点)
    欧拉图:存在欧拉回路的图。判断无向图为欧拉图的充要条件是所有点的度数均为偶数。
    半欧拉图:存在欧拉通路的图。判断无向图为欧拉图的充要条件是所有点的度数均为偶数或只有两个点的度数为奇数
    一个图中如果存在度数为奇数的点,那么这样的点一定有偶数个。

 

  让我们先从最简单的说起——它是一整个连通图。

    如果他自己已经是一个环,输出0即可。

    如果他是一个欧拉图(我们刨去为环的情况),那么就是所有入度大于2的点的个数,我们只要把它们都烧熔掉为若干个入读为2的点就好了。

    对于其他情况,我们可以想到,对于入度大于2的点,我们依然要去烧熔掉,而对于入度为单数的点,当我们将入度大于2的点全部融完之后会出现 单数入度点数/2条 链我们还要把它们接到一起,所以答案就是 入度大于2的点数+入度为单数的点数/2;

  下面说更复杂的——它是若干个连通图。

    此时如果他自己是一个环,那么我们就需要把它融开,使它成为一条链和其他链去拼接在一起,ans++,sum(链数)++。

    如果他是一个欧拉图,那么我们看似要把它先融成一个环,再把它拆开,但我们完全可以在融某一个点时就直接把它融开。所以ans+=入度大于2的点数,sum++。

    对于其他情况,我们由于只需要把它们融成链,所以,ans+=入读大于2的点数,sum+=入读为单数的点数/2。

   在最后我们输出ans+sum即可。

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <map>
#include <vector>
#define N 1005
#define M 50005*2+1005
using namespace std;
int n,m,zz,rd[M],a[M],zz2;
struct ro
{
	int to;
	int next;
}road[100005];
void build(int x,int y)
{
	zz++;
	road[zz].to=y;
	road[zz].next=a[x];
	a[x]=zz;
}
vector<int> q1[M];
int dfn[M],low[M],st[M],top,zz3,zz4;
bool rz[M],rd2[M];
void tar(int x)
{
	zz4++;
	dfn[x]=low[x]=zz4;
	rz[x]=rd2[x]=1;
	top++;st[top]=x;
	for(int i=a[x];i>0;i=road[i].next)
	{
		int y=road[i].to;
		if(!rd2[y])
		{
			tar(y);
			low[x]=min(low[x],low[y]);
		}
		else if(rz[y])
		{
			low[x]=min(low[x],dfn[y]);
		}
	}
	if(dfn[x]==low[x])
	{
		zz3++;
		int v;
		do{
			v=st[top];
			rz[v]=0;
			top--;
			q1[zz3].push_back(v);
		}while(dfn[v]!=low[v]);
	}
} 
int main()
{
	scanf("%d%d",&n,&m);
	zz2=n;
	for(int i=1;i<=m;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		if(!x)
		{
			zz2++;
			x=zz2;
		}
		if(!y)
		{
			zz2++;
			y=zz2;
		}
		build(x,y),build(y,x);
		rd[x]++,rd[y]++;
	}
	n=zz2;
	for(int i=1;i<=n;i++)
	{
		if(rd[i]&&!rd2[i])
			tar(i);
	}
	int ans=0,sum=0;
	for(int i=1;i<=zz3;i++)
	{
		int js1=0,js2=0;
		for(int j=0;j<q1[i].size();j++)
		{
			if(rd[q1[i][j]]%2)js1++;
			if(rd[q1[i][j]]>2)js2++;
		}
		if(zz3==1)
		{
			if(js1)
			{
				ans=js2+js1/2;
			}
			else ans=js2;
			break;
		}
		if(js1==0&&js2==0)
		{
			ans++;
			sum++;
		}
		else if(js1==0)
		{
			sum++;
			ans+=js2;
		}
		else
		{
			ans+=js2;
			sum+=js1/2;
		}
	}
	ans+=sum;
	printf("%d\n",ans);
	return 0;
}

  

10.1 国庆节给祖国母亲献礼 NOIP 模拟题 第二题题解

标签:href   简单   时间   lin   ++   pac   除了   UI   rip   

原文地址:http://www.cnblogs.com/liutianrui/p/7617597.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!