欧拉道路
在每条边只能走一次的情况下,遍历图中所有的边
欧拉回路
在每条边只能走一次的情况下,遍历图中所有的边然后回到原点
欧拉道路判定
连通块中恰好具有 两个 奇度节点时(起点和终点)为欧拉道路!
欧拉回路判定
连通块中所有节点都是 偶度节点时为欧拉回路(基于dfs进行判定)
例题1
清理道路
给出n个城市
每对城市都有一条双向通道
现在给出m条需要清理的道路
给出m行,每行 给出该道路 所连接的两个城市
我们可以从任意城市出发,要求走完所有的m条道路
问最少次数是多少
这就需要用到 欧拉道路 的知识
如果一个这m条边组成的图正好是一个欧拉道路,不就说明每条边走一次就可以恰好遍历每条边吗,这样走的最少次数就是m次!
考虑图的联通问题
在这个题中,我们的图是构建在一个大图当中的一个子图
所以会存在连通块不只一个的情况
但是本题说,任意两个城市之间都有一条双向的道路
所以只要在两个连通块之间 加 1条边即可!
统计联通块的个数,额外加的边数=连通块的个数-1
图不联通
建一座额外的桥
考虑不是欧拉道路的情况
比如这个图,所有节点的度数都是3,并且有4个,显然不是一个欧拉道路,不可能一次性不重复走完所有的边,但是我们要求最短次数,怎么办?
我们可以假设在任意两点之间,又创建了一个新边,这样一条边可以让两个
度数为奇数的点 变成 偶数度,所以针对 一个连通块中,奇数度点X时,我们只需要额外建边(假想)最终使其的奇数度点数变成两个(欧拉道路)这样仍然是最短路径,额外建边的数量=(x-2)/2
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+5;
int du[N];
vector<int> ve[N];//这是个技巧的地方,在利用 dfs遍历图时,可以先用vector二维存一下
// 每个节点联通的方向,否则暴力搜索会超时。这样可以直接找到每个城市所联通的城市!
int vis[N];
int ans,ans1;
int dfs(int x)//利用深搜判断联通块中的 度数 个数!
{
int sum=0;///度数为奇数的 节点的个数!!
vis[x]=1;
for(int i=0;i<ve[x].size();i++)
{
if(!vis[ve[x][i]])
sum+=dfs(ve[x][i]);
}
if(du[x]%2)
return sum+1;
return sum;
}//联通块中奇数 度节点的个数!
int main()
{
int n,m,t;
int num=1;
while(cin>>n>>m>>t&&(n||m||n))
{
for(int i=0;i<=n;i++)
ve[i].clear();
memset(du,0,sizeof du);
memset(vis,0,sizeof vis);
for(int i=0;i<m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
du[a]++;
du[b]++;
ve[a].push_back(b);
ve[b].push_back(a);
}
ans=0;//连通块的个数!
ans1=0;
for(int i=1;i<=n;i++)
{
if(du[i]&&!vis[i])
//如果度为0,说明这个点不在这个连通块中!,vis[i]=1,说明节点被访问过!
//已经在连通块中被访问过了!
{
ans++;
int x=dfs(i);//x为联通块 节点中 奇数度的个数!!!
if(x>2)
ans1+=(x-2)/2;
//这里时本题关键内容!
//在连通块里建边,让他变成欧拉回路
//统计奇度点的个数X,目的是让 个数变成2
//所以新建的边数 就是 (x-2)/2!
}
}
if(ans>0)
ans--;
//ans是联通块的个数 -1;
//这里统计连通块的个数
//是由于,我们要在两个连通块之间搭建一条 “桥 ” 让两个连通块相通!!!
//如果修理的道路中不在一个连通块中,那么就不可避免地要 在连通块之间搭桥!
cout<<ans<<" "<<ans1<<" "<<m<<endl;
printf("Case %d: %d\n",num++,t*(ans+ans1+m));//至少要走m条路
}
return 0;
}