{
简单的说如果一个图存在一笔画(一笔能连完),则一笔画的路径叫做欧拉路,如果最后又回到起点,那这个路径叫做欧拉回路。
存在欧拉回路的图称为欧拉图,存在欧拉路径但不存在欧拉回路的图称为半欧拉图。
奇点:跟这个点相连的边数目有奇数个的的点。
存在欧拉路的条件:图是连通的,有且只有2 个奇点。
存在欧拉回路的条件:图是连通的,有0 个奇点。
两个定理的正确性是显而易见的,既然每条边都要经过一次,那么
对于欧拉路,除了起点和终点外,每个点如果进入了一次,显然一定要
出去一次,显然是偶点。
对于欧拉回路,每个点进入和出去次数一定都
是相等的,显然没有奇点。
如果寻找欧拉回路,对任意一个点进行dfs,寻找欧拉路,则对一个度为1 的点进行dfs,时间复杂度为O(m + n),m为边数,n 为点数。
☆骑马修栅栏
描述 Description
农民John每年有很多栅栏要修理。他总是骑着马穿过每一个栅栏并修复它破损的地方。
John是一个与其他农民一样懒的人。他讨厌骑马,因此从来不两次经过同一个栅栏。你必须编一个程序,读入栅栏网络的描述,并计算出一条修栅栏的路径,使每个栅栏都恰好被经过一次。John能从任何一个顶点(即两个栅栏的交点)开始骑马,在任意一个顶点结束。
每一个栅栏连接两个顶点,顶点用1到500标号(虽然有的农场并没有500个顶点)。一个顶点上可连接任意多(>=1)个栅栏。所有栅栏都是连通的(也就是你可以从任意一个栅栏到达另外的所有栅栏)。
你的程序必须输出骑马的路径(用路上依次经过的顶点号码表示)。我们如果把输出的路径看成是一个500进制的数,那么当存在多组解的情况下,输出500进制表示法中最小的一个 (也就是输出第一个数较小的,如果还有多组解,输出第二个数较小的,等等)。
输入格式 Input Format
第1行: 一个整数F(1 <= F <= 1024),表示栅栏的数目
第2到F+1行: 每行两个整数i, j(1 <= i,j <= 500)表示这条栅栏连接i与j号顶点。
输出格式 Output Format
输出应当有F+1行,每行一个整数,依次表示路径经过的顶点号。注意数据可能有多组解,但是只有上面题目要求的那一组解是认为正确的。
样例输入 Sample Input
9
1 2
2 3
3 4
4 2
4 5
2 5
5 6
5 7
4 6
样例输出 Sample Output
1
2
3
4
2
5
4
6
5
7
时间限制 Time Limitation
1s
代码
#include
#define MAXN 1050
using namespace std;
int g[MAXN][MAXN],dd[MAXN];
int n,m,F;
stack<int> v;
void dfs(int x)
{
for(int i = 1; i <= n; ++i)
{
if(g[x][i])
{
g[x][i]--;
g[i][x]--;
dfs(i);
}
}
v.push(x);
}
int main()
{
cin>>F;
int x,y;
for(int i = 1; i <= F; ++i)
{
cin>>x>>y;
n = max(n,x);
n = max(n,y);
g[x][y]++
g[y][x]++;
dd[x]++;
dd[y]++;
}
int s = 1;
for(int i = 1; i <= n; ++i)
{
if(dd[i]%2 == 1)
{
s = i;
break;
}
}
dfs(s);
while(!v.empty())
{
cout<<v.top()<<endl;
v.pop();
}
return 0;
}
}
{
欧拉回路是指不重复地走过所有的路径并且最后还能回到起点的回
路。
哈密尔顿环是指不重复地走过所有的点并且最后还能回到起点的回
路。
步骤:
① 判断奇点数。
奇点数若为0则任意指定起点,奇点数若为2则指定起点为奇点。
②开始递归函数Hierholzer(x):
循环寻找与x相连的边(x,u):
删除(x,u)
删除(u,x)
Hierholzer(u);
将x插入答案队列
void hierholzer(int x){
for(int i = begin; i <= end; ++i){
if(a[x][i] >= 1){
a[x][i] --;
a[i][x] --;
euler(i);
}
}ans[++ansszie] = x;
}
}
{
现在来考虑一个问题:
现在有一个工程,这个工程被分成了很多部分。有一些部分要求前面某些部分完成后才可以开始进行。有些部分则可以同时进行。我们可以把每个部分看作一个结点,刚刚这些限制看成是边。
比如说这张图就可以看成是这样一个限制。
这样的图有个特点:没有环。
因此这样的图也被成为DAG(有向无环图)。
求出一个这个工程的工作序列的算法被称为拓扑排序。
拓扑排序的过程大概是这样的:
① 选择一个入度为0 的结点并直接输出
② 删除这个结点以及与它关联的所有边。
③ 重复步骤①和②,直到找不到入度为0 的结点。
通常情况下,在实现的时候会维护一个队列以及每个结点的入度。
在删除边的时候顺便把相应结点的入度减去,当这个结点入度为0 的时候直接将其加入队
void toposort(){
queue< int > q;
for(int i = 1; i <= n; ++i)
if(!d[i]) q.push(i);
while(!q.empty()){
int now = q.front();q.pop();
ans[++cnt] = now;
for(int i = lin[now]; i ; i <= e[i].ne){
int y = e[i].y;
d[y]--;
if(!d[y]) q.push(y);
}
}
if(cnt != n) puts("-1");//如果答案不够n个点
else for(int i = 1; i <= n; ++i)
printf("%d\n",ans[i]);//输出排序后的顺序
}
}
{
差分约束系统(system of difference constraints),是求解关于一
组变数的特殊不等式组之方法。如果一个系统由n个变量和m 个约束条
件组成,其中每个约束条件形如xj − xi ≤ bk (i, j ∈ [1, n], k ∈ [1, m]),则称
其为差分约束系统(system of difference constraints)。差分约束系统是求
解关于一组变量的特殊不等式组的方法。
通俗一点地说,差分约束系统就是一些不等式的组,而我们的目标
是通过给定的约束不等式组求出最大值或者最小值或者差分约束系统是
否有解
嗯,然而听起来和图论并没有什么关系。
但是看看这个不等式,是不是和松弛操作中的if(dis[x] + z < dis[y] ) dis[y] = dis[x] + z有点像?
其实之所以差分约束系统可以通过图论的最短路来解,是因
为xj − xi <= bk类似最短路中的三角不等式dis[y] ≤ d[x] +z,
即dis[y] − dis[x] ≤ z。而求取最大值的过程类似于最短路算法中的松
弛过程。
三角不等式:
B − A ≤ c
C − B ≤ a
C − A ≤ b
如果要求C − A的最大值,可以知道为min(b, a + c),而这正对应
了C到A的最短路。
对三角不等式加以推广,变量n个,不等式m个,要求xn − x1的最大
值,便就是求取建图后的最短路。
同样地,如果要求取差分约束系统中xn − x1的最小值,便是求取建
图后的最长路。
注意,建图后不一定存在最短路/最长路,因为可能存在无限减
小/增大的负环/正环,题目一般会对应于不同的输出。判断差分约束系
统是否存在解一般判环即可。
我是挖坑小王子༺༺坑༒坑༻༻
回来补
逃了