2021年百度之星初赛二 1003.魔怔【欧拉路】

题目链接

题意

给一张无向完全图,每条边有一个颜色,为黑色或者白色。你初始在点 S S S上,你每次可以从当前的点经过一条边走到另一个点上,并将走过的边的颜色翻转。你想要把图中所有的边全都变为黑色,要求最小化走过的边的条数,求这个最小值,或者判断无解。

题解

首先思考怎样才能让所有边变黑:

走完一个全是白边的块 C C 1 CC1 CC1 将其变黑 —> 走黑边 E E E 到另外一个白边块 C C 2 CC2 CC2 —> 走完白边块 C C 2 CC2 CC2 将其变黑 —> 原路返回把 E E E 变回黑色

重复以上步骤

所以

无解的情况有:

1. 所有白边中,奇数度顶点 数量不为 02

感性思考一下,对于每个块,奇数度顶点数量一定都是 022k

0 ,即这个块为一个简单环,即有欧拉回路
2 ,即这个块只有欧拉通路而没有欧拉回路
2k,则这个块不存在欧拉通路

不存在欧拉通路那显然无解,这个块的白边都走不完

如果奇数度顶点总数为 0,那么所有块都是简单环,显然有解。从起始块到下一个块,走一遍欧拉回路就可以回到起始块,而且起始块可以随便选

如果奇数度顶点总数为 2,那么起始块只能是那个 非简单环 的块。因为假如一个非简单环不是起始块,那走完这个非简单环的欧拉通路,就无法回到起始块

如果奇数度顶点总数大于2,那就肯定有一个以上的非简单环的块

2.奇数度顶点数量为2,但是起点S不是奇数度顶点

前面说了,如果奇数度顶点总数为 2,那么起始块只能是那个 非简单环 的块。假如起点 S S S 不是奇数度顶点,说明
(1)S 不在非简单环的块内
(2)或者 S 不是非简单环的欧拉通路的起点/终点
无论是哪种情况,那个非简单环都无法走完,就是走完了也无法回到起始块

有解的情况有:

1.起点S在一个块内

那就把这个块作为起始点,要走的边数量有:

所有白边数 C 0 C0 C0 + 块之间的黑边数 * 2 ,不难算得黑边数为块的数量 CC-1

2.起点S不在一个块内

那就要先走到一个块,然后和上面一样,最后再走回S,即:

所有白边数 C 0 C0 C0 + 块之间的黑边数 * 2 + 2

#include<iostream>
#include<sstream>
#include<string>
#include<queue>
#include<map>
#include<unordered_map>
#include<set>
#include<vector>
#include<stack>
#include <utility>
#include<list>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<time.h>
#include<random>
using namespace std;

#define int long long
#define PI acos(-1.0)
#define eps 1e-9
#define lowbit(a) ((a)&-(a))
#define mid ((l+r)>>1)
#define mem(x,y) memset(x,y,sizeof x)

const int mod = 1e9+7;
int qpow(int a,int b){
	int ans=1;
	while(b){
		if(b&1)ans=(ans*a)%mod;
		a=(a*a)%mod;
		b>>=1;
	}
	return ans;
}
const int INF = 0x3f3f3f3f;
const int N = 1e5+10;
int n,S; 
int deg[N],f[N];
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
void join(int x,int y){f[find(x)]=find(y);}
#define endl '\n'
signed main(){
	std::ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	int t; cin>>t;
	while(t--){
		int c0=0;
		cin>>n>>S; for(int i=1;i<=n;i++)f[i]=i,deg[i]=0;
		for(int i=2;i<=n;i++)
			for(int j=1,x;j<i;j++){
				char ch; cin>>ch; x=ch-'0';
				if(!x)deg[i]++,deg[j]++,c0++,join(i,j);
			}
		int flag=deg[S]&1,cnt=0,cc=0;
		for(int i=1;i<=n;i++){
			if(deg[i]&1)cnt++;
			if(deg[i])cc+=find(i)==i;
		}
		if((cnt!=2&&cnt!=0)||(cnt==2&&!flag)){cout<<-1<<endl;continue;}
		if(deg[S])cout<<(cc-1)*2+c0<<endl;
		else cout<<cc*2+c0<<endl;
	}
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值