CF858F Wizard's Tour

也许更好的阅读体验

D e s c r i p t i o n \mathcal{Description} Description

给定一张 n n n 个点 m m m 条边的无向图,每条边连接两个顶点,保证无重边自环,不保证连通。

你想在这张图上进行若干次旅游,每次旅游可以任选一个点 x x x 作为起点,再走到一个与 x x x 直接有边相连的点 y y y,再走到一个与 y y y 直接有边相连的点 z z z 并结束本次旅游。

作为一个旅游爱好者,你不希望经过任意一条边超过一次,注意一条边不能即正向走一次又反向走一次,注意点可以经过多次,在满足此条件下,你希望进行尽可能多次的旅游,请计算出最多能进行的旅游次数并输出任意一种方案。

S o l u t i o n \mathcal{Solution} Solution

20分思路

先提供一个比较傻且只能得20分的思路
就是我们把每条边看做是一个点,距离为一的点之间连一条边
于是问题就变成了求最大匹配了
不过这样会把边的条数大大增大…
妥妥的TLE

100分思路

若仅是一棵树,那此题的做法还是很显然的
要保证边用的最多,按照树的深度从小到大考虑,即按照拓扑序将能匹配的匹配就是正确的

若不仅是一棵树,我们随便按照一种方式把它的生成树建出来
这样就有非树边和树边,对于每个点,我们先将其与父亲的边不考虑
设其周围有 n n n条边
n n n为偶数,就可以把它们两两搭配,有 n 2 \frac{n}{2} 2n种方法
n n n为奇数,就拿一条边与其与父亲的边搭配,剩下的两两搭配
显然,这样做除了在根节点剩下一条边,其他的边都会被用到

C o d e \mathcal{Code} Code

实现部分说一下吧
我觉得我打得比其它人的简洁一些吧
大部分人用了一个 v e c t o r vector vector去记录哪些边
实际上,我们可以直接把这些边匹配
f [ x ] f[x] f[x]表示是否有一条与 x x x相连且还没有匹配的边
每次拿到新边看看有没有为匹配的边,有的话它们就匹配
注意一条边只需考虑一次

/*******************************
Author:Morning_Glory
LANG:C++
Created Time:2019年08月30日 星期五 09时08分56秒
*******************************/
#include <cstdio>
#include <fstream>
#include <cstring>
using namespace std;
const int maxn = 1000006;
//{{{cin
struct IO{
	template<typename T>
	IO & operator>>(T&res){
		res=0;
		bool flag=false;
		char ch;
		while((ch=getchar())>'9'||ch<'0')	 flag|=ch=='-';
		while(ch>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch^'0'),ch=getchar();
		if (flag)	 res=~res+1;
		return *this;
	}
}cin;
//}}}
int n,m,u,v,cnt,ans;
int head[maxn],nxt[maxn],to[maxn];//edge
int a[maxn],b[maxn],c[maxn],f[maxn];//ans
bool vis[maxn];
//{{{add
void add (int u,int v)
{
	nxt[cnt]=head[u],head[u]=cnt,to[cnt++]=v;
}
//}}}
//{{{dfs
void dfs (int x)
{
	vis[x]=true;
	for (int e=head[x];~e;e=nxt[e]){
		int te=to[e];
		to[e]=to[e^1]=0;
		if (te){
			if (!vis[te])	dfs(te);
			if (f[te])	a[++ans]=x,b[ans]=te,c[ans]=f[te],f[te]=0;
			else if (f[x])	a[++ans]=f[x],b[ans]=x,c[ans]=te,f[x]=0;
			else	f[x]=te;
		}
	}
}
//}}}
int main()
{
	memset(head,-1,sizeof(head));
	cin>>n>>m;
	for (int i=1;i<=m;++i)	cin>>u>>v,add(u,v),add(v,u);
	vis[0]=true;
	for (int i=1;i<=n;++i)
		if (!vis[i])	dfs(i);
	printf("%d\n",ans);
	for (int i=1;i<=ans;++i)	printf("%d %d %d\n",a[i],b[i],c[i]);
	return 0;
}

如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值