[CSP-S模拟测试]:Miner(欧拉路)

题目背景

$And\ the\ universe\ said\ you\ are\ the\ daylight \\ And\ the\ universe\ said\ you\ are\ the\ night \\ And\ the\ universe\ said\ the\ darkness\ you\ fight\ is\ within\ you \\ And\ the\ universe\ said\ the\ light\ you\ seek\ is\ within\ you$


题目描述

    在$Minecraft$中,小$C$最喜欢做的工作就是挖矿,但他从不自己挖矿洞,因为这样每挖不久就会遇到铁矿挡到面前,而小$C$是不屑于打铁的;于是他总是利用天然矿洞,但这样很容易迷路或者走到重复的路径,降低了他收割$Au$的效率,于是他希望对路径进行规划。
    在小$C$的眼里,矿洞是一个$n$个点的无向图,其中有$m$条无向通道连接。小$C$可以从任选一个起点开始,进行一下操作中的一个:
    $0\ v$,选择一条相邻的还未经过的通道走到$v$。
    $1\ v$,利用修改器,直接传送到任意一个点$v$。
    小$C$希望在经过的所有通道恰好一次的前提下,最小化传送的次数。输出这个最小次数,并给出任意一个最优方案。


输入格式

第一行两个整数$n,m$,接下来$m$行,每行两个整数描述一条无向边。


输出格式

首先输出一个整数表示答案$ans$。
接下来一行,输出你选择的起点$S$。
接下来$ans+m$行,每行两个证书描述一个操作,格式见问题描述。


样例

样例输入:

5 4
1 2
1 2
3 4
5 5

样例输出:

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


题解

    题意就是加上尽量少的边,使图中存在欧拉路径;也就使用尽量少的路径,覆盖所有边恰好一次,假设途中有$k$个联通块,第$i$个中有$c_i$个度数为奇数的点,那么答案为:$\sum \limits_{i=1}^k\max(1,\frac{c_i}{2})-1$。
    构造方法也很简单,将度数为奇数的点任意配对连额外的边,然后每个联通块跑欧拉回路,额外连的边将回路割成若干段路径,这些路径之间以及联通块之间用$1$操作跳即可。

时间复杂度:$\Theta(n+m)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
struct rec
{
	int nxt;
	int to;
}e[1000001];
int head[100001],cnt=1;
int n,m;
int du[100001];
bool vis[100001],vib[1000001];
int sta[1000001],wzc[1000001];
long long sum,cmax;
long long ans=-1;
int tot;
pair<int,int> pos[1000001];
void add(int x,int y)
{
	e[++cnt].nxt=head[x];
	e[cnt].to=y;
	head[x]=cnt;
}
void dfs(int x)
{
	vis[x]=1;
	if(du[x]&1)sta[++sta[0]]=x;
	for(int i=head[x];i;i=e[i].nxt)
		if(!vis[e[i].to])dfs(e[i].to);
}
void ola(int x)
{
	wzc[0]=1;
	wzc[1]=x;
	while(wzc[0])
	{
		nxt:x=wzc[wzc[0]];
		for(int i=head[x];i;i=e[i].nxt)
			if(!vib[i])
			{
				vib[i]=vib[i^1]=1;
				wzc[++wzc[0]]=e[i].to;
				head[x]=e[i].nxt;
				goto nxt;
			}
		sta[++sta[0]]=x;
		wzc[0]--;
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	while(m--)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
		du[x]++;
		du[y]++;
	}
	for(int i=1;i<=n;i++)
		if(!vis[i]&&du[i])
		{
			sta[0]=0;
			dfs(i);
			head[0]=0;
			if(!sta[0])
			{
				add(i,0);
				add(i,0);
				add(0,i);
				add(0,i);
			}
			else
				while(sta[0])
				{
					add(sta[sta[0]],0);
					add(0,sta[sta[0]--]);
				}
			ola(0);
			while(sta[0]>1)
				if(sta[sta[0]])pos[++tot]=make_pair(0,sta[sta[0]--]);
				else
				{
					sta[0]--;
					pos[++tot]=make_pair(1,sta[sta[0]--]);
					ans++;
				}
		}
	printf("%d\n%d\n",ans,pos[1].second);
	for(int i=2;i<=tot;i++)
		printf("%d %d\n",pos[i].first,pos[i].second);
	return 0;
}

rp++

转载于:https://www.cnblogs.com/wzc521/p/11396603.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值