【题解】相框

问题 B: 相框
时间限制: 1 Sec 内存限制: 256 MB
题目描述
【问题描述】
P大的基础电路实验课是一个无聊至极的课。每次实验,T君总是提前完成,管理员却不让T君离开,T君只能干坐在那儿无所事事。
先说说这个实验课,无非就是把几根导线和某些元器件(电阻、电容、电感等)用焊锡焊接起来。
为了打发时间,T君每次实验做完后都在焊接一些诡异的东西,这就是他的杰作:
T君不满足于焊接奇形怪状的作品,强烈的破坏欲驱使他拆掉这个作品,然后将之焊接成规整的形状。这会儿,T君正要把这个怪物改造成一个环形,当作自己的相框,步骤如下:
T君约定了两种操作:

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

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

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

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

【输入文件】
输入文件的第一行共有两个整数n和m:分别表示原有的作品的焊点和导线的数量 (0 ≤ n ≤ 1 000, 2 ≤ m ≤ 50 000)。焊点的标号为1~n。

接下来的m行每行共有两个整数:导线两端所连接的两个焊点的标号,若不与任何焊点相连,则将这一端标号为0。

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

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

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

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

【输入样例1】
6 8

1 2

1 3

3 4

1 4

4 6

5 6

4 5

1 5

【输出样例1】
4

【输入样例2】
0 2

0 0

0 0

【输出样例2】
2

【输入样例3】
3 3

0 1

0 0

2 2

【输出样例3】
4

【数据规模和约定】
30%的数据中n≤10;

100%的数据中n≤1000。

欧拉回路:

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

解析:

图片自己去找。。。
因为最终情况要满足所有点的度数都是2(度数一开始就是0的点直接扔掉)所以我们要对所有度数大于2的点都要被熔烧。奇数的熔成一堆2度数的点+一个单个的点。
那么现在有两种情况

  1. 只有一个联通块,也就是所有边都连在一起。只要把所有度数大于2的点熔成度数<=2的点,再把所有单个的点(熔出来的+本来就有的)两两配对组一起即可。自己试一下就是这样的,所以没必要管谁和谁连了。
  2. 有多个联通块,就要把所有的联通块都变成环,然后把所有环再熔成一条链,再把所有链连起来。。。对于过程中出现1度数点的联通块只要少熔和一对就好了。如果没有1度数点,但在过程中有把偶数点熔成2度数点的过程,就可以把一对2度数点熔成两个单个的就好了。

联通块用并查集就行了。
但是那堆0很令人尴尬.。。每个0新建一个点就好了。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=50000*2+1001;

//sum:单链个数
//ans:熔点的次数
//flag[]:是否有熔点操作
//cut[]:是否有奇数点
//in[]:度数
//tot:点数

void read(int &x) {
    
    
	x=0;int f=1;char c=getchar();
	while(c<'0'||c>'9') {
    
    if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9') {
    
    x=x*10+c-'0';c=getchar();}
}



int n,m,in[N],fa[N],tot=n,con,sum,ans,cut[N],flag[N];
//ans是熔点的个数,sum是单链的个数(单链两端度数一定为奇,熔点不改变其它点的度)
//这里就要求我们熔点时不产生自环(是否特判?)
//多个联通块则分别单独操作再累加,只需特判是否本来是环(+1) 


int find(int x) {
    
    
	if(fa[x]!=x) fa[x]=find(fa[x]);
	return fa[x];
}


int main() {
    
    
	read(n),read(m);
	tot=n;
	for(int i=1;i<=2*m+n;i++) fa[i]=i;
	for(int i=1,x,y;i<=m;i++) {
    
    
		read(x),read(y);
		if(x==0) x=++tot;
		if(y==0) y=++tot;
		int u=find(x),v=find(y);
		if(u!=v) fa[u]=v;
		in[x]++,in[y]++;
	}
	for(int i=1;i<=tot;i++)
	    if(fa[i]==i&&in[i]) con++;
	if(con==1) {
    
    
		//printf("yes");
		for(int i=1;i<=tot;i++) {
    
    
			if(!in[i]) continue;
			if(in[i]&1) sum++;
			if(in[i]>2) ans++;
		}
	}
	else {
    
    
		for(int i=1;i<=tot;i++) {
    
    
			if(!in[i]) continue;
			if(in[i]&1) sum++,cut[find(i)]=1;
			if(in[i]>2) ans++,flag[find(i)]=1;
		}
		for(int i=1;i<=tot;i++) {
    
    
			if(in[i]&&fa[i]==i&&!cut[i]) {
    
    
				ans++;
				if(!flag[i]) ans++;
			}
		}
	}
	printf("%d",ans+sum/2);
} 

猜你喜欢

转载自blog.csdn.net/cqbzlydd/article/details/108794114