图论——欧拉图

部分概念:

欧拉通路:每条路径只走一次的条件下,走完所有的边

欧拉回路:满足欧拉通路的条件下,能走回起点。

所有的边仅走一次,不论节点   (一笔画游戏)

//满足的条件     定义 ind[ i ] , outd[ i ] 为 i 点的入度与出度

有向图中,存在欧拉通路,当且仅当:

1.图中所有 非0 度数的点连通

2.最多只有 一个 点的 outd[ i ]=ind[ i ]+1

3.最多只有一个点的ind[ i ]=outd[ i ]+1

ps: 当 所有点的  入度==出度  时,存在欧拉回路

寻找并存储欧拉图的代码实现

单词接龙 - 题目 - Daimayuan Online Judge

//注意题中 尾部字母与首部字母相同可相连  用欧拉定理可解释为:

//将每个单词当作一条边,所有点加入可形成欧拉回路 !!注意要每个点都加入

 那么每个单词的首尾字母当成节点 形成一个 首->尾的有向边

V<int>h[27];
//出度同样可看成每个点连出去的边数
int ind[27], outd[27];
int n = 26, m;
//C数组倒序记录欧拉路径(dfs性质)   l记录C数组的元素个数
//f数组记录当前节点已经遍历的边数
int f[27],c[N],l;

//x起点开始寻找欧拉回路
void dfs(int x)
{
	while (f[x] < outd[x])
	{
		int y = h[x][f[x]];
		f[x]++;
		dfs(y);
		c[++l] = y;
	}

}
void work()
{
	int x, y, z;
	x = y = z = 0;
	for (int i = 1; i <= 26; i++)
	{
		if (ind[i] != outd[i])z++;
		if (ind[i]+1 == outd[i])y++,x=i;
	}
    //欧拉路的判定定理
	if (!(!z || z == 2 && y == 1))
	{
		cout << "No" << endl;return; 
	}
	else
	{
		if (!z)
		{
			for(int i=1;i<=n;i++)
				if (ind[i])
				{
					x = i; break;
				}
		}
		memset(f, 0, sizeof(f));
		dfs(x);
       //在dfs中 起点没有存入 节点数比边数多一个
		c[++l] = x;
      //特判欧拉路是否存有所有节点
		if (l == m + 1)
			cout << "Yes";
		else cout << "No";
		cout << endl;
       //可打印出欧拉路径
      //for(int i=l;i;i--)
          //cout<<c[i]<<" ";
	}
}
int main()
{
	cin >> m;
	for(int i=1;i<=m;i++)
	{
		string s; cin >> s;
		int x = s[0] - 'a' + 1,y=s[s.size()-1]-'a'+1;
		h[x].push_back(y);
		ind[y]++; 
		outd[x]++;
	}
	work();
}

在无向图中的欧拉路:

无向图中 每有一条边 则 两端点 度数加1

 成立欧拉路的条件

两个点的度数为奇数 或  所有点度数为偶数(欧拉回路)  

 欧拉路判断 - 题目 - Daimayuan Online Judge

 ps:小技巧  无向边 拆分成 两条有向边 

当遍历过其中一条时, 如何标记另一条 使这两边不在被访问呢? 

// 用 {2 3} {4,5} {6 7}……方式存储相邻边  
//相邻两边 xor 1 是可以标记另一条有向边
//例如   2^1=3 3^1=2   5^1=4 4^1=5 
int  cnt=1
//m为边数  c存图方式

struct node
{
	int  y, idx;
};
vector<node>h[N];

for (int i = 1; i <= m; i++)
	{
		int x = read(), y = read();
		h[x].push_back({ y,++cnt });
		h[y].push_back({ x,++cnt });
		d[x]++, d[y]++;
	}

//标记方式
if (!vis[idx])
		{
			vis[idx ^ 1] = 1;
		}

核心:

struct node
{
	int  y, idx;
};

void dfs(int x)
{
	while (f[x] < d[x])
	{
		int y = h[x][f[x]].y, idx = h[x][f[x]].idx;
		f[x]++;
		if (!vis[idx])
		{
			vis[idx ^ 1] = 1;
			dfs(y);
			c[++l] = y;
		}
	}

}

void work()
{
	//y记录奇度点个数
	int x = 0, y = 0;
	for (int i = 1; i <= n; i++)
		if (d[i] & 1)
			x = i, y++;
	//无欧拉回路
	if (y && y != 2)
	{
		cout << "No" << endl; return;
	}
	if (!y)
		//寻找起点
		for (int i = 1; i <= n; i++)
			if (d[i])
			{
				x = i; break;
			}
	memset(f, 0, sizeof(f));
	memset(vis, false, sizeof(vis));
	l = 0;
	dfs(x);
	c[++l] = x;
	if (l == m + 1)
	{
		cout << "Yes";
	}
	else cout << "No";
	cout << endl;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zzz0929_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值