部分概念:
欧拉通路:每条路径只走一次的条件下,走完所有的边
欧拉回路:满足欧拉通路的条件下,能走回起点。
所有的边仅走一次,不论节点 (一笔画游戏)
//满足的条件 定义 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;
}