彻底弄懂欧拉路问题

彻底弄懂欧拉路问题

石头于2014.11.23整理

起源历史

图论起源

图论起源于18世纪,1736年瑞士数学家欧拉(Euler)发表了图论的第一篇论文“哥尼斯堡七桥问题”。在当时的哥尼斯堡城有一条横贯全市的普雷格尔河,河中的两个岛与两岸用七座桥连结起来。当时那里的居民热衷于一个难题:有游人怎样不重复地走遍七桥,最后回到出发点。 为了解决这个问题,欧拉用A,B,C,D4个字母代替陆地,作为4个顶点,将联结两块陆地的桥用相应的线段表示,于是哥尼斯堡七桥问题就变成了图中,是否存在经过每条边一次且仅一次,经过所有的顶点的回路问题了。欧拉在论文中指出,这样的回路是不存在的。

一笔划:

⒈凡是由偶点组成的连通图,一定可以一笔画成。画时可以把任一偶点为起点,最后一定能以这个点为终点画完此图。

⒉凡是只有两个奇点的连通图(其余都为偶点),一定可以一笔画成。画时必须把一个奇点为起点,另一个奇点终点。

⒊其他情况的图都不能一笔画出。(奇点数除以二便可算出此图需几笔画成。)

基本概念:

欧拉通路 (欧拉迹)—通过图中每条边一次且仅一次,并且过每一顶点的通路。

欧拉回路 (欧拉闭迹)—通过图中每条边一次且仅一次,并且过每一顶点的回路。

欧拉图—存在欧拉回路的图。欧拉图就是从一顶出发每条边恰通过一次又能回到出发顶点的那种图,即不重复的行遍所有的边再回到出发点。

通路和回路-称vie1e2envj为一条从 vi到 vj且长度为n的通路,其中长度是指通路中边的条数.称起点和终点相同的通路为一条回路。

简单图-不含平行边和自回路的图。

混合图-既有有向边,也有无向边的图

平凡图-仅有一个结点的图

完全图-有n个结点的且每对结点都有边相连的无向简单图,称为无向完全图;有n个结点的且每对结点之间都有两条方向相反的边相连的有向简单图为有向完全图。

 

欧拉生平

口欧拉(Euler)瑞士数学家,贡献遍及数学各领域,是数学史上最伟大的 数学家之一。生于公元1707415日。他的父亲保罗•欧拉(Paul Euler)是一名加尔文教派的教师,但欧拉在大学求学期间在雅各.伯 努利(Jacob Bernoulli)家住过并从雅各身上学了不少数学。

口保罗希望欧拉读神学,却犯了最大的错误,在欧拉很小的时候便教他 数学,挑动了他内心中的数学灵魂。而欧拉他最好的朋友就是大数学 家约翰.伯努利(John Bernoulli,雅各的弟弟)。约翰说:「欧拉注定

要成为大数学家,而非牧师。」最后保罗终于在约翰之劝说下同意欧 拉攻读数学。从此展开他灿烂的学术生涯,并成为数学史上最伟大的 数学家之一。

口欧拉对于数学的贡献是全面性的,基本上我们可以称他是一个百科全 书型的数学家。「他是有史以来瑞士最多产的科学家,也是一个不可 思议的数学幻想家,他在任何领域都能发现数学,在任何情况都能进 行研究。...

口欧拉一生都是在科学院度过。首先是在俄国的圣彼得堡科学院,1740 年后则在柏林科学院待到59岁。由于与腓特列大帝相处的问题,离开 柏林,接受凯萨琳女皇二世邀请再次前往圣彼得堡,一直到他过世( 1783年)。科学院的工作让他可以专心研究数学,全心全意地将整个 生命投入,就好像牧师将生命奉献给上帝一般。

口相对于牛顿的内向、退缩、神经质,欧拉则是乐观且仁慈宽厚,甚至 在1771年眼睛完全瞎掉,仍保有乐观的性格,在几乎完全失明之下, 仍藉由口述给他的助理(实际上就是他的儿子),来继续数学创作。

(其中包括需要烦琐计算之航海天文学的贡献)在后续的17年间欧拉 继续发展着数学,如果说有什么不同,那就是他比以前做得更多。他 的智慧使他巧妙地把握各种概念和想法而无需将它们书写在纸上,他 非凡的记忆力,使他的头脑有如一个堆满知识的图书馆。

口欧拉、牛顿与莱布尼兹都是属于新数学理论的开拓者,有 人将欧拉、高斯、黎曼在数学的地位比喻为乐坛上的三B :巴哈、贝多芬、布拉姆斯,也有人将欧拉比拟为数学界 的莎士比亚:

普世性、巨细靡遗、取之不尽、用之不竭。

口 Read Euler, read Euler, he is the master of us all.

P.-S.de Laplace

欧拉图的特征

 无向图

aG有欧拉通路的充分必要条件为:连通,G中只有两个奇度顶点(它们分别是欧拉通路的两个端点)

bG有欧拉回路(G为欧拉图)G连通,G中均为偶度顶点。 

 有向图

aD有欧拉通路:D连通,除两个顶点外,其余顶点的入度均等于出度,这两个特殊的顶点中,一个顶点(终点)的入度比出度大1,另一个顶点(起点)的入度比出度小1

bD有欧拉回路(D为欧拉图)D连通,D中所有顶点的入度等于出度。一个有向图是欧拉图,当且仅当该图所有顶点度数都是0

现代扩展

对欧拉图的一个现代扩展是蜘蛛图,它向欧拉图增加了可以连接的存在点。这给予欧拉图析取特征。欧拉图已经有了合取特征(就是说区定义了有着与起来的那些性质的对象在区中的存在)。所以蜘蛛图允许使用欧拉图建模逻辑或的条件。

相关定理

1.无向连通图G是欧拉图,当且仅当G不含奇数度结点(G的所有结点度数为偶数)

2.无向连通图G含有欧拉通路,当且仅当G有零个或两个奇数度的结点;

3.有向连通图D是欧拉图,当且仅当该图为连通图且D中每个结点的入度=出度

4.有向连通图D含有欧拉通路,当且仅当该图为连通图且D中除两个结点外,其余每个结点的入度=出度,且此两点满足(起始点s的入度=出度-1,结束点t的出度=入度-1 或两个点的入度=出度)

5.一个非平凡连通图是欧拉图当且仅当它的每条边属于奇数个环。 

6.如果图G是欧拉图且 H = G - uv,则H有奇数个u,v-迹仅在最后访问v;同时,在这一序列的u,v-迹中,不是路径的迹的条数是偶数。(Todia[1973])//??????????

弗罗莱(Fleury)算法思想-解决欧拉回路

    Fleury算法:

   任取v0V(G),令P0=v0

Pi=v0e1v1e2ei vi已经行遍,按下面方法从中选取ei+1

aei+1vi相关联;

b)除非无别的边可供行遍,否则ei+1不应该为Gi=G-{e1,e2, , ei}中的桥(所谓桥是一条删除后使连通图不再连通的边);

c)当(b)不能再进行时,算法停止。

可以证明,当算法停止时所得的简单回路Wm=v0e1v1e2.emvm(vm=v0)G中的一条欧拉回路,复杂度为O(e*e)

欧拉算法的C实现

实现一

#include "SqStack.h" //堆栈的常见操作
#include "Queue.h"//队列的常见操作
 
typedef int Graph[200][200];
int v,e;
 
void DFS(Graph &G,SqStack &S,int x,int t)
{
       int k=0,i,m,a;
       Push(S,x);
       for(i=t;i0)
              {
                     k=1;
                     G[i][x]=0; //删除此边
                     G[x][i]=0;
                     DFS(G,S,i,0);
                     break;
              }//if,for
       if(k==0)
       {
              Pop(S);
              GetTop(S,m);
              G[x][m]=1;//恢复刚刚删除的边
              G[m][x]=1;
              a=x+1;//从下一条边开始搜寻
              if(StackLength(S)!=e)
              {
                     Pop(S);
                     DFS(G,S,m,a);
              }//if
              else
                     Push(S,x);
       }//if
}//DFS
int BFSTest(Graph G)
{
       int a[200],x,i,k=0;
       LinkQueue Q;
       InitQueue(Q);									//见附录 linkqueue
       EnQueue(Q,0);
       for(i=0;i0)
                            if(a[i]!=1)
                            {
                                   a[i]=1;
                                   EnQueue(Q,i);
                            }//if
       }//while
       for(i=0;iv%d",m);
              Pop(S);
       }//while
}
 
void InputM1(Graph &G)
{
 
int h,z;
printf("Please input 顶点数和边数/n");
scanf("%d",&v);
scanf("%d",&e);
for(int i=0;i

实现二

//无向图欧拉通路(num==2)、欧拉回路(num==0)Fleury模板:

#include 
#include 
#include 
#define MAXN 200
using namespace std;
struct stack{
    int top,node[MAXN];
}s;
int Edge[MAXN][MAXN];
int n;
void dfs(int x){
    int i;
    s.top++;
    s.node[s.top]=x;
    for(i=0;i0){
            Edge[i][x]=0;
            Edge[x][i]=0;
            dfs(i);
            break;
        }
    }
}
void Fleury(int x){
    int i,b;
    s.top=0;s.node[s.top]=x;
    while(s.top>=0){
        b=0;
        for(i=0;i0){
                b=1;break;
            }
        }
        if(b==0){
            printf("%d ",s.node[s.top]+1);
            s.top--;
        }
        else{
            s.top--;
            dfs(s.node[s.top+1]);
        }
    }
    printf("\n");
}
int main()
{
    freopen("in.txt","r",stdin);
    int i,j;
    int m,s,t;
    int degree,num,start;
    while(scanf("%d%d",&n,&m)!=EOF){
        memset(Edge,0,sizeof(Edge));
        for(i=0;i

/*          1**********2********3    

Input: *    *  *        *               

9 14        *     *    *        *       

1 2         *   *      *        *       

1 8  8**********9********4

2 3         *          *      * *

2 8         *          *   *    *

2 9         *          * *      *

3 4         7**********6********5

4 5E[0][1]==0 E[0][7]==0

4 6E[1][0]==0 E[1][2]==0 E[1][7]==0 E[1][8]==0

4 9E[2][1]==0 E[2][3]==0

5 6E[3][2]==0 E[3][4]==0 E[3][5]==0 E[3][8]==0

6 7E[4][3]==0 E[4][5]==0

6 9E[5][3]==0 E[5][4]==0 E[5][6]==0 E[5][8]==0

7 8E[6][5]==0 E[6][7]==0

8 9E[7][0]==0 E[7][1]==0 E[7][6]==0 E[7][8]==1

E[8][1]==0 E[8][3]==0 E[8][5]==0 E[8][7]==1

*/

 

Output:

 

1 8 9 6 7 8 2 9 4 6 5 4 3 2 1

求解混合图欧拉回路的一般方法
1、随意定向


在混合图中,对于双向边的处理除了拆边之外,还有任意定向。先对全图的双向边进行任意定向,接着使用上文的欧拉回路算法,很显然,无法得到结果。但是通过这一步,至少可以确定这样一件事实,如果一个点的出度加入度一定是奇数的话,那么这个图一定没有欧拉回路。


而随意定向是没有依据的,但是可以使用这样的随机化处理方法,再使用恰当的调整方法构造出解。
2、自调整方法


所谓的自调整方法就是将其中的一些边的方向调整回来,使所有的点的出度等于入度。但是有一条边的方向改变后,可能会改变一个点的出度的同时改变另一个点的入度,相当于一条边制约着两个点。同时有些点的出度大于入度,迫切希望它的某些点出边转向;而有些点的入度大于出度,迫切希望它的某些入边转向。这两条边虽然需求不同,但是他们之间往往一条边转向就能同时满足二者。
具体步骤:


1、另x = |入度-出度|/2;对于不同的点有不同的x值,这个x值代表它们在邻接表中相应调整x条就能让出度等于入度。


2、以把图中的点转换为一个二分图,每个点的x值就是它们的点权。


3、置源点S向所有出度>入度的点连边;设置汇点T,所有入度大于出度的点连边,将各自的点权转换为边权。


4、最后将原图中所有暂时定向的无向边加上一个1的容量,方向不变,而有向边不能改变方向,不需连边。


可以发现,从源点S出发的一个单位流将会一个“无向边”的容量变为0,使得两端的点权各自减1,其实这就是在模拟一次对无向边方向的调整。当把图建好后,依靠最大流性质可以最大可能地无冲突调整边的方向,并最终使得每个点的点容量都达到满流。


最后,还要对那些图中出度等于入度的点做适当分析,它们作为一个“中间点”,由于流平衡性质,不会留下任何流量值,对于那些真正需要调整的点不会带来任何影响。


最后,如何得到答案?那就是检查从源点出发的每条边是否都满流,如果有一条边没有满流,说明有一个点没有调整到入度等于出度,于是整个图不存在欧拉回路。


具体的题目有: POJ 1637、 Hdu 3472
图的点连通度边连通度总结
点连通度的定义:一个具有N个点的图G中,在去掉任意k-1个顶点后(1<=k<=N),所得的子图仍然连通,去掉K个顶点后不连通,则称G是K连通图,K称作图G的连通度,记作K(G)。
独立轨:A,B是图G(有向无向均可)的两个顶点,我们称为从A到B的两两无公共内顶的轨为独立轨,其最大的条数记作p(A,B)。


在上图中有一个具有7个定点的连通图,从顶点1到顶点3有3条独立轨,即p(1,3)=3;
1—2—3 ,   1—7—3 , 1—6—5—4—3
如果分别从这3条独立轨中,每条轨抽出一个内点,在G图中删掉,则图不连通。若连通图G的两两不相邻顶点间的最大独立轨数最小的P(A,B)值即为K(G)。若G为完全图(两两点可达),则
K(G)=n-1,即完全把某个点的所有边删掉后才不连通。既然独立轨是只能经过一次的边,那么可以构造网络流模型,其中每条边的容量为1,就可以限制只经过一次。
构建网络流模型:
若G为无向图:
(1)原G图中的每个顶点V变成N网中的两个顶点V`和V``,顶点V`至V``有一条弧容量为1;
(2)原图G中的每条边e=UV,在N网中有两条弧e`=U``V`,e``=V``U`与之对应,e`与e``容量均为无穷;
(3)以A``为源点,B`为汇点,求最大流。
若G为有向图
(1)原G图中的每个顶点V变成N网中的两个顶点V`和V``,顶点V`至V``有一条容量为1的弧;
(2)原G图中的每条弧e=UV变成一条有向轨U`U``V`V``,其中轨上的弧U``V`的容量为无穷;
(3)以A``为源点,B`为汇点求最大流。
上面的模型只是求出了以A为源点B为汇点的最大流max_flow,等价于在G中只要去掉max_flow个点就会使得A与B不连通。而图的连通度是要求去掉最少的点使得整个图不连通,做法是固定一个点为源点,枚举与源点不相邻的点为汇点,求最大流。在所有的枚举结果中最小的max_flow值就是要求的K(G).注意如果某次枚举的汇点求出 的最大流为无穷则说明此此枚举的源点与汇点是强连通的。如果所有的枚举结果都为无穷,则说明整个图G是强连通的,需要去掉n-1个点才能破坏其连通性。
所有具有流量为1的弧(V`,V``)对应的V顶点组成一个割顶集
通过求连通度可以得到一个结论:G是K的连通图,k>=2,则任意K个顶点共圈。
求边连通度总结:
同样引入独立轨的概念,只是在这里叫弱独立轨,同样在每条弱独立轨中只有去掉某一条边就可以使起点到终点不连通,现在整个图G的边连通度就是要找出任意两点的弱独立轨的最小值。如果图G为完全图,则K`(G)为n-1。
构建一个网络N
若G为无向图:
1.     原G图中的每条边e=UV变成两条边e`=UV,e``=VU,容量都为1;
2.     固定一个点为源点,枚举与源点不相邻的为汇点,求最大流max_flow,保留最小的max_flow即为图的边连通度。
若G为有向图:
1.     原G图中每条有向边容量为1;
2.     此步骤与无向图的步骤2相同。
求出的残余网络中,流量为1的弧e`=(u,v),则e`就是桥。
从图的边连通度中可以得到以下结论:
1.          A是有向图G的一个顶点,如果A与G的其他所有点V间的最小值为K,则G中存在以A为根的K棵无公共边的生成树;
2.          设G是有向图,0 附:求无向图边连通度代码

#include 
#include 
#include 
#include 
#include 
using namespace std;

const int MAXN = 105;
const int MAXM = 105*105;
const int INF = 0x3f3f3f3f;

struct Edge
{
	int v, next;
	int f;
}edge[MAXM];

int cnt;
int n, m;

int first[MAXN], level[MAXN];
int q[MAXN];

void init()
{
	cnt = 0;
	memset(first, -1, sizeof(first));
}

void read_graph(int u, int v, int f)
{
	edge[cnt].v = v, edge[cnt].f = f;
	edge[cnt].next = first[u], first[u] = cnt++;
	edge[cnt].v = u, edge[cnt].f = 0;
	edge[cnt].next = first[v], first[v] = cnt++;
}

int bfs(int s, int t)
{
	memset(level, 0, sizeof(level));
	level[s] = 1;
	int front = 0, rear = 1;
	q[front] = s;
	while(front < rear)
	{
		int x = q[front++];
		if(x == t) return 1;
		for(int e = first[x]; e != -1; e = edge[e].next)
		{
			int v = edge[e].v, f = edge[e].f;
			if(!level[v] && f)
			{
				level[v] = level[x] + 1;
				q[rear++] = v;
			}
		}
	}
	return 0;
}

int dfs(int u, int maxf, int t)
{
	if(u == t) return maxf;
	int ret = 0;
	for(int e = first[u]; e != -1; e = edge[e].next)
	{
		int v = edge[e].v, f = edge[e].f;
		if(level[v] == level[u] + 1 && f)
		{
			int Min = min(maxf-ret, f);
			f = dfs(v, Min, t);
			edge[e].f -= f;
			edge[e^1].f += f;
			ret += f;
			if(ret == maxf) return ret;
		}
	}
	return ret;
}

int Dinic(int s, int t)
{
	int ans = 0;
	while(bfs(s, t)) ans += dfs(s, INF, t);
	return ans;
}

void read_case()
{
	init();
	while(m--)
	{
		int u, v;
		scanf("%d%d", &u, &v);
		read_graph(u, v, 1);
		read_graph(v, u, 1);
	}
}

void solve()
{
	read_case();
	int ans = INF;
	for(int i = 2; i <= n; i++) //以1为源点枚举 
	{
		int ans = min(ans, Dinic(1, i));
	}
}

int main()
{
	while(scanf("%d%d", &n, &m))
	{
		solve();
	}
	return 0;
}

欧拉通路与哈密顿通路的区别
欧拉通路:如果图中存在一条通过图中各边一次且仅一次的通路,则称此回路为欧拉通路,具有欧拉通路的图称为半欧拉图。


哈密尔顿通路:给定n个点及n个点两两之间的距离(或权数),求一条回路,使之经过所有的点,且经过每个点仅一次,而整条回路(也称路径或边界)的总距离(或总权数)最小。

欧拉回路习题
程序实现一般是如下过程:


1.利用并查集判断图是否连通,即判断p[i] < 0的个数,如果大于1,说明不连通。


2.根据出度入度个数,判断是否满足要求。


3.利用dfs输出路径。


比较好的题目是poj2337,判断单词是否连成一排的。
POJ1637
Sightseeing tour
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 7826 Accepted: 3268
Description


The city executive board in Lund wants to construct a sightseeing tour by bus in Lund, so that tourists can see every corner of the beautiful city. They want to construct the tour so that every street in the city is visited exactly once. The bus should also start and end at the same junction. As in any city, the streets are either one-way or two-way, traffic rules that must be obeyed by the tour bus. Help the executive board and determine if it's possible to construct a sightseeing tour under these constraints.
Input


On the first line of the input is a single positive integer n, telling the number of test scenarios to follow. Each scenario begins with a line containing two positive integers m and s, 1 <= m <= 200,1 <= s <= 1000 being the number of junctions and streets, respectively. The following s lines contain the streets. Each street is described with three integers, xi, yi, and di, 1 <= xi,yi <= m, 0 <= di <= 1, where xi and yi are the junctions connected by a street. If di=1, then the street is a one-way street (going from xi to yi), otherwise it's a two-way street. You may assume that there exists a junction from where all other junctions can be reached.
Output


For each scenario, output one line containing the text "possible" or "impossible", whether or not it's possible to construct a sightseeing tour.
Sample Input


4
5 8
2 1 0
1 3 0
4 1 1
1 5 0
5 4 1
3 4 0
4 2 1
2 2 0
4 4
1 2 1
2 3 0
3 4 0
1 4 1
3 3
1 2 0
2 3 0
3 2 0
3 4
1 2 0
2 3 1
1 2 0
3 2 0
Sample Output


possible
impossible
impossible
possible
Source


Northwestern Europe 2003
Hdu3472
HS BDC


Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 918    Accepted Submission(s): 371




Problem Description
IELTS is around the corner! love8909 has registered for the exam, but he still hasn’t got prepared. Now he decides to take actions. But when he takes out the New Oriental IELTS Vocabulary, he finds there are so many words. But love8909 doesn’t get scared, because he has got a special skill. If he can make a list with some meaningful words, he will quickly remember these words and will not forget them. If the last letter of some word Wa is the same as the first letter of some word Wb, then you can connect these two words and make a list of two words. If you can connect a word to a list, you will make a longer list.


While love8909 is making the list, he finds that some words are still meaningful words if you reverse them. For example, if you reverse the word “pat”, you will get another meaningful word “tap”.


After scanning the vocabulary, love8909 has found there are N words, some of them are meaningful if reversed, while others are not. Now he wonders whether he can remember all these words using his special skill.


The N-word list must contain every word once and only once.
 


Input
An integer T (T <= 50) comes on the first line, indicating the number of test cases.


On the first line of each test cases is an integer N (N <= 1000), telling you that there are N words that love8909 wants to remember. Then comes N lines. Each of the following N lines has this format: word type. Word will be a string with only ‘a’~’z’, and type will be 0(not meaningful when reversed) or 1(meaningful when reversed). The length of each word is guaranteed to be less than 20.


 


Output
The format of the output is like “Case t: s”, t is the number of the test cases, starting from 1, and s is a string.
For each test case, if love8909 can remember all the words, s will be “Well done!”, otherwise it’s “Poor boy!”


 


Sample Input
3
6
aloha 0
arachnid 0
dog 0
gopher 0
tar 1
tiger 0
3
thee 1
earn 0
nothing 0
2
pat 1
acm 0
 


Sample Output
Case 1: Well done!
Case 2: Well done!
Case 3: Poor boy!


Hint
In the first case, the word “tar” is still meaningful when reversed, and love8909 can make a list as “aloha-arachnid-dog-gopher-rat-tiger”.
 In the second case, the word “thee” is still meaningful when reversed, and love8909 can make a list as “thee-earn-nothing”. In the third case, 
no lists can be created.
 
 


Author
allenlowesy
 


Source
2010 ACM-ICPC Multi-University Training Contest(4)——Host by UESTC
 


Recommend
zhengfeng


hdu3018
Ant Trip


Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1713    Accepted Submission(s): 662




Problem Description
Ant Country consist of N towns.There are M roads connecting the towns.


Ant Tony,together with his friends,wants to go through every part of the country. 


They intend to visit every road , and every road must be visited for exact one time.However,it may be a mission impossible for only one group of people.So they are trying to divide all the people into several groups,and each may start at different town.Now tony wants to know what is the least groups of ants that needs to form to achieve their goal.


 


Input
Input contains multiple cases.Test cases are separated by several blank lines. Each test case starts with two integer N(1<=N<=100000),M(0<=M<=200000),indicating that there are N towns and M roads in Ant Country.Followed by M lines,each line contains two integers a,b,(1<=a,b<=N) indicating that there is a road connecting town a and town b.No two roads will be the same,and there is no road connecting the same town.
 


Output
For each test case ,output the least groups that needs to form to achieve their goal.
 


Sample Input
3 3
1 2
2 3
1 3


4 2
1 2
3 4
 


Sample Output
1
2


Hint




New ~~~ Notice: if there are no road connecting one town ,tony may forget about the town.
In sample 1,tony and his friends just form one group,they can start at either town 1,2,or 3.
In sample 2,tony and his friends must form two group.


 
 


Source
2009 Multi-University Training Contest 12 - Host by FZU
 


Recommend
gaojie


给出N个节点,M个边,问要遍历一遍所有的边,需要的最小group数目。
求一个图中最少几笔画,利用欧拉回路性质,首先得到图的每个强连通分支,然后计算每个强连通分支每个是否都是偶数度是的话,一笔解决,否的话,需要奇数度节点个数的1/2笔解决。(当一个节点度数为奇数时,我们只需要令它的度数为1,因为偶数的话直接抵消了,最后判断一个scc中未1的节点个数,除以2就得到该scc的最小笔画了)
poj1386
Play on Words
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 9838 Accepted: 3386
Description


Some of the secret doors contain a very interesting word puzzle. The team of archaeologists has to solve it to open that doors. Because there is no other way to open the doors, the puzzle is very important for us. 


There is a large number of magnetic plates on every door. Every plate has one word written on it. The plates must be arranged into a sequence in such a way that every word begins with the same letter as the previous word ends. For example, the word ``acm'' can be followed by the word ``motorola''. Your task is to write a computer program that will read the list of words and determine whether it is possible to arrange all of the plates in a sequence (according to the given rule) and consequently to open the door. 
Input


The input consists of T test cases. The number of them (T) is given on the first line of the input file. Each test case begins with a line containing a single integer number Nthat indicates the number of plates (1 <= N <= 100000). Then exactly Nlines follow, each containing a single word. Each word contains at least two and at most 1000 lowercase characters, that means only letters 'a' through 'z' will appear in the word. The same word may appear several times in the list.
Output


Your program has to determine whether it is possible to arrange all the plates in a sequence such that the first letter of each word is equal to the last letter of the previous word. All the plates from the list must be used, each exactly once. The words mentioned several times must be used that number of times. 
If there exists such an ordering of plates, your program should print the sentence "Ordering is possible.". Otherwise, output the sentence "The door cannot be opened.". 
Sample Input


3
2
acm
ibm
3
acm
malform
mouse
2
ok
ok
Sample Output


The door cannot be opened.
Ordering is possible.
The door cannot be opened.
Source


Central Europe 1999
给出n个单词,如果一个单词的尾和另一个单词的头字符相等,那么可以相连,问这n个单词是否可以排成一列。欧拉路应用,构图:一个单词的头尾字母分别作为顶点,每输入一个word,该word的头指向word的尾画一个有向边,并且记录每个顶点的出入度。利用并查集先判断是否为scc,如果是的话则判断是否奇数度节点为0或者只有2个。
poj2230
Watchcow
Time Limit: 3000MS Memory Limit: 65536K
Total Submissions: 6146 Accepted: 2650Special Judge
Description


Bessie's been appointed the new watch-cow for the farm. Every night, it's her job to walk across the farm and make sure that no evildoers are doing any evil. She begins at the barn, makes her patrol, and then returns to the barn when she's done. 


If she were a more observant cow, she might be able to just walk each of M (1 <= M <= 50,000) bidirectional trails numbered 1..M between N (2 <= N <= 10,000) fields numbered 1..N on the farm once and be confident that she's seen everything she needs to see. But since she isn't, she wants to make sure she walks down each trail exactly twice. It's also important that her two trips along each trail be in opposite directions, so that she doesn't miss the same thing twice. 


A pair of fields might be connected by more than one trail. Find a path that Bessie can follow which will meet her requirements. Such a path is guaranteed to exist.
Input


* Line 1: Two integers, N and M. 


* Lines 2..M+1: Two integers denoting a pair of fields connected by a path.
Output


* Lines 1..2M+1: A list of fields she passes through, one per line, beginning and ending with the barn at field 1. If more than one solution is possible, output any solution.
Sample Input


4 5
1 2
1 4
2 3
2 4
3 4
Sample Output


1
2
3
4
2
1
4
3
2
4
1
Hint


OUTPUT DETAILS: 


Bessie starts at 1 (barn), goes to 2, then 3, etc...
Source


USACO 2005 January Silver
题目大意:给出n个field及m个连接field的边,然后要求遍历每条边仅且2次,求出一条路径来。
这个题目典型欧拉回路,由于题目保证了肯定存在,所以我们直接dfs就行,首先有个小技巧是如何判断该条路是否走过了,也就是我们得对有向边进行标记。利用之前的结构
struct edge{
    int next;
    int to;
};
edge node[maxm];
int adj[maxv] = {-1}
每一条边对应了一个next,我们只需要对next标记就可以了。
poj2337
Catenyms
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 9832 Accepted: 2561
Description


A catenym is a pair of words separated by a period such that the last letter of the first word is the same as the last letter of the second. For example, the following are catenyms: 
dog.gopher


gopher.rat


rat.tiger


aloha.aloha


arachnid.dog


A compound catenym is a sequence of three or more words separated by periods such that each adjacent pair of words forms a catenym. For example, 


aloha.aloha.arachnid.dog.gopher.rat.tiger 


Given a dictionary of lower case words, you are to find a compound catenym that contains each of the words exactly once.
Input


The first line of standard input contains t, the number of test cases. Each test case begins with 3 <= n <= 1000 - the number of words in the dictionary. n distinct dictionary words follow; each word is a string of between 1 and 20 lowercase letters on a line by itself.
Output


For each test case, output a line giving the lexicographically least compound catenym that contains each dictionary word exactly once. Output "***" if there is no solution.
Sample Input


2
6
aloha
arachnid
dog
gopher
rat
tiger
3
oak
maple
elm
Sample Output


aloha.arachnid.dog.gopher.rat.tiger
***
Source


Waterloo local 2003.01.25
求欧拉路径和poj1386同一个题,只不过这个题目需要输出欧拉路径。
题目大意:给出一组单词,如果两个单词,一个单词的头和另一个单词的尾相同,则可以相连,例如abce, efdg,可以相连,问这组单词能否排成一排,如果可以求出字典序自小的那个。
构图:单词作为边,单词的头字母和尾字母分别作为顶点,读入一个单词,添加一条边,并且用邻接表来存边,首先利用并查集判断图是否连通,然后再判断是否可以构成欧拉通路或者回路,如果是回路,则从a开始找,找到第一个存在的字母,从这个字母遍历就行,如果是通路,则必须从出度大于入度1的那个顶点开始遍历。由于这个题目要求最小字典顺序,然后考虑我们建立邻接表的时候是采用头插法,那么我们如果将单词从小到大排序,由于我们遍历顶点肯定是从小的顶点开始的,这样遍历一个顶点的邻接边的时候就会从大到小访问这个顶点的边,无法满足字典最小,所以从大到小排序,遍历依node数组为准,每次遍历一条边设置该下标已访问过。
HDU1878
欧拉回路


Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 9583    Accepted Submission(s): 3467




Problem Description
欧拉回路是指不令笔离开纸面,可画过图中每条边仅一次,且可以回到起点的一条回路。现给定一个图,问是否存在欧拉回路?
 


Input
测试输入包含若干测试用例。每个测试用例的第1行给出两个正整数,分别是节点数N ( 1 < N < 1000 )和边数M;随后的M行对应M条边,每行给出一对正整数,分别是该条边直接连通的两个节点的编号(节点从1到N编号)。当N为0时输入结
束。
 


Output
每个测试用例的输出占一行,若欧拉回路存在则输出1,否则输出0。
 


Sample Input
3 3
1 2
1 3
2 3
3 2
1 2
2 3
0
 


Sample Output
1
0
 


Author
ZJU
 


Source
浙大计算机研究生复试上机考试-2008年
Poj 1041 John's trip
John's trip
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 7266 Accepted: 2394Special Judge
Description


Little Johnny has got a new car. He decided to drive around the town to visit his friends. Johnny wanted to visit all his friends, but there was many of them. In each street he had one friend. He started thinking how to make his trip as short as possible. Very soon he realized that the best way to do it was to travel through each street of town only once. Naturally, he wanted to finish his trip at the same place he started, at his parents' house. 


The streets in Johnny's town were named by integer numbers from 1 to n, n < 1995. The junctions were independently named by integer numbers from 1 to m, m <= 44. No junction connects more than 44 streets. All junctions in the town had different numbers. Each street was connecting exactly two junctions. No two streets in the town had the same number. He immediately started to plan his round trip. If there was more than one such round trip, he would have chosen the one which, when written down as a sequence of street numbers is lexicographically the smallest. But Johnny was not able to find even one such round trip. 


Help Johnny and write a program which finds the desired shortest round trip. If the round trip does not exist the program should write a message. Assume that Johnny lives at the junction ending the street appears first in the input with smaller number. All streets in the town are two way. There exists a way from each street to another street in the town. The streets in the town are very narrow and there is no possibility to turn back the car once he is in the street 
Input


Input file consists of several blocks. Each block describes one town. Each line in the block contains three integers x; y; z, where x > 0 and y > 0 are the numbers of junctions which are connected by the street number z. The end of the block is marked by the line containing x = y = 0. At the end of the input file there is an empty block, x = y = 0.
Output


Output one line of each block contains the sequence of street numbers (single members of the sequence are separated by space) describing Johnny's round trip. If the round trip cannot be found the corresponding output block contains the message "Round trip does not exist."
Sample Input


1 2 1
2 3 2
3 1 6
1 2 5
2 3 3
3 1 4
0 0
1 2 1
2 3 2
1 3 3
2 4 4
0 0
0 0
Sample Output


1 2 3 5 4 6 
Round trip does not exist.
Source


Central Europe 1995
题解:用并查集判断连通性(也可以用搜索)。然后用DFS求欧拉路径即可。欧拉回路有一个这样的性质,我们从图G中去掉一个圈得到新图G‘有欧拉回路,那么G也有欧拉回路,基于这个性质,一旦找到一个圈就消去,从图中拿出,直到图为空。另一种算法Fleury可以放下吧。。。


Uva 10054 The Necklace


  Problem D: The Necklace 
My little sister had a beautiful necklace made of colorful beads. Two successive beads in the necklace shared a common color at their meeting point. The figure below shows a segment of the necklace:




But, alas! One day, the necklace was torn and the beads were all scattered over the floor. My sister did her best to recollect all the beads from the floor, but she is not sure whether she was able to collect all of them. Now, she has come to me for help. She wants to know whether it is possible to make a necklace using all the beads she has in the same way her original necklace was made and if so in which order the bids must be put.


Please help me write a program to solve the problem.


Input 


The input contains T test cases. The first line of the input contains the integer T.


The first line of each test case contains an integer N (  $5 \le
N \le 1000$) giving the number of beads my sister was able to collect. Each of the next N lines contains two integers describing the colors of a bead. Colors are represented by integers ranging from 1 to 50.


Output 


For each test case in the input first output the test case number as shown in the sample output. Then if you apprehend that some beads may be lost just print the sentence ``some beads may be lost" on a line by itself. Otherwise, print N lines with a single bead description on each line. Each bead description consists of two integers giving the colors of its two ends. For  $1 \le i \le N ­ 1$, the second integer on line i must be the same as the first integer on line i + 1. Additionally, the second integer on line N must be equal to the first integer on line 1. Since there are many solutions, any one of them is acceptable.


Print a blank line between two successive test cases.


Sample Input 


2
5
1 2
2 3
3 4
4 5
5 6
5
2 1
2 2
3 4
3 1
2 4
Sample Output 


Case #1
some beads may be lost
 
Case #2
2 1
1 3
3 4
4 2
2 2




Miguel Revilla 
2000-12-28
题解:每个颜色当作一个节点,每个珠子当作一条边。然后求此无向图的欧拉回路。
Poj 2762 Going from u to v or from v to u?
Going from u to v or from v to u?
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 14701 Accepted: 3886
Description


In order to make their sons brave, Jiajia and Wind take them to a big cave. The cave has n rooms, and one-way corridors connecting some rooms. Each time, Wind choose two rooms x and y, and ask one of their little sons go from one to the other. The son can either go from x to y, or from y to x. Wind promised that her tasks are all possible, but she actually doesn't know how to decide if a task is possible. To make her life easier, Jiajia decided to choose a cave in which every pair of rooms is a possible task. Given a cave, can you tell Jiajia whether Wind can randomly choose two rooms without worrying about anything?
Input


The first line contains a single integer T, the number of test cases. And followed T cases. 


The first line for each case contains two integers n, m(0 < n < 1001,m < 6000), the number of rooms and corridors in the cave. The next m lines each contains two integers u and v, indicating that there is a corridor connecting room u and room v directly. 
Output


The output should contain T lines. Write 'Yes' if the cave has the property stated above, or 'No' otherwise.
Sample Input


1
3 3
1 2
2 3
3 1
Sample Output


Yes
Source


POJ Monthly--2006.02.26,zgl & twb
题意是判断一个有向图图是否能任取两点u,v都能够使得从u能够到达v,或者使得从v能够到达u。那么其实本题就是判断单连通性。


针对本题而言,首先我们使用Tarjan算法求出强连通分量,然后在缩点求出对应的Dag图,然后我们对此Dag进行拓扑排序,确定此Dag是否具有唯一的拓扑排序,如果不是,说明此图不存在欧拉回路。


附录
部分定理证明
在以下讨论中,假设图不存在孤立点[ 度为0的顶点称为孤立点。];否则,先将所有孤立点从图中删除。显然,这样做并不会影响图中欧拉回路的存在性。
我们经常需要判定一个图是否为欧拉图(或半欧拉图),并且找出一条欧拉回路(或欧拉路径)。对于无向图有如下结论:
定理1 无向图为欧拉图,当且仅当为连通图且所有顶点的度为偶数。
证明 必要性。设图的一条欧拉回路为。由于经过图的每一条边,而图没有孤立点,所以也经过图的每一个顶点,为连通图成立。而对于图的任意一个顶点,经过时都是从一条边进入,从另一条边离开,因此经过的关联边的次数为偶数。又由于不重复地经过了图的每一条边,因此的度为偶数。
充分性。假设图中不存在回路,而是连通图,故一定是树,那么有。由于图所有顶点的度为偶数而且不含孤立点,那么图的每一个顶点的度至少为2。由握手定理,有,与假设相矛盾。故图中一定存在回路。设图中边数最多的一条简单回路[ 边没有重复出现的回路称为简单回路。]为


下面证明回路是图的欧拉回路。
假设不是欧拉回路,则中至少含有一个点,该点的度大于经过该点的关联边的次数。令,从出发有一条不属于的边。若,则顶点自身构成一个环,可以将其加入中形成一个更大的回路;否则,若,由于的度为偶数,而中经过的关联边的次数也是偶数,所以必然存在一条不属于的边。依此类推,存在不属于的边。故是一条新的回路,将其加入中可以形成一个更大的回路,这与是图的最大回路的假设相矛盾。故是图的欧拉回路。
由定理1可以立即得到一个用于判定半欧拉图的推论:
推论1 无向图为半欧拉图,当且仅当为连通图且除了两个顶点的度为奇数之外,其它所有顶点的度为偶数。
证明 必要性。设图的一条欧拉路径为


  由于经过图的每一条边,而图没有孤立点,所以也经过图的每一个顶点,为连通图成立。对于顶点,进入的次数比离开的次数少1;对于顶点,进入的次数比离开的次数多1:故和的度为奇数。而对于其它任意一个顶点,进入的次数等于离开的次数,故的度为偶数。
充分性。设是图中唯一的两个度为奇数的顶点。给图加上一条虚拟边得到图,则图的每一个顶点度均为偶数,故图中存在欧拉回路。从中删去得到一条从到的路径,即为图的欧拉路径。
对于有向图,可以得到类似的结论:
定理2 有向图为欧拉图,当且仅当的基图[ 忽略有向图所有边的方向,得到的无向图称为该有向图的基图。]连通,且所有顶点的入度等于出度。
推论2 有向图为半欧拉图,当且仅当的基图连通,且存在顶点的入度比出度大1、的入度比出度小1,其它所有顶点的入度等于出度。
这两个结论的证明与定理1和推论1的证明方法类似,这里不再赘述。
注意到定理1的证明是构造性的,可以利用它来寻找欧拉回路。下面以无向图为例,介绍求欧拉回路的算法。
首先给出以下两个性质:
性质1 设是欧拉图中的一个简单回路,将中的边从图中删去得到一个新的图,则的每一个极大连通子图都有一条欧拉回路。
证明 若为无向图,则图的各顶点的度为偶数;若为有向图,则图的各顶点的入度等于出度。
性质2 设、是图的两个没有公共边,但有至少一个公共顶点的简单回路,我们可以将它们合并成一个新的简单回路。
证明 只需按如图3所示的方式合并。


由此可以得到以下求欧拉图的欧拉回路的算法:
1在图中任意找一个回路;
2将图中属于回路的边删除;
3在残留图的各极大连通子图中分别寻找欧拉回路;
4将各极大连通子图的欧拉回路合并到中得到图的欧拉回路。


LinkQueue
using namespace std;


#include


#define OK 1
#define ERROR -1
#define TRUE 1
#define FALSE 0
#define OVERFLOW -2


typedef char QElemType;
typedef struct QNode {
/************************************************************************/
/*单链队列存储结构         */
/*Went 2011-11-2 10:08           */
/************************************************************************/
QElemType data;
struct QNode *next;
}QNode, *QueuePtr;
typedef struct {
QueuePtr front;
QueuePtr rear;
}LinkQueue;


int InitQueue(LinkQueue &Q) {
//initiate an empty queue
Q.front = Q.rear = (QueuePtr)malloc(sizeof(QNode));
if (!Q.front)
return OVERFLOW;
Q.front -> next = NULL;
return OK;
}


int DestroyQueue(LinkQueue &Q) {
//destroy an existed queue
if (Q.front == NULL)
return ERROR;
while(Q.front) {
Q.rear = Q.front -> next;
free(Q.front);
Q.front = Q.rear;
}
return OK;
}


int ClearQueue(LinkQueue &Q) {
//clear an exitstd queue
if (Q.front == NULL) 
return ERROR;
QueuePtr p, q;
Q.rear = Q.front;
p = Q.front -> next;
Q.front -> next = NULL;
while(p) {
q = p;
p = p -> next;
free(q);
}
return OK;
}


int QueueEmpty(LinkQueue Q) {
//whether the queue is empty
if (Q.front == NULL)
return ERROR;
else if (Q.front -> next == NULL)
return TRUE;
else
return FALSE;
}


int QueueLenth(LinkQueue Q) {
//get the length of the queue
if (Q.front == NULL)
return ERROR;
int len = 0;
QueuePtr p;
p = Q.front;
while(p != Q.rear) {
len ++;
p = p -> next;
}
return len;
}




int GetHead(LinkQueue Q, QElemType &e) {
//get the head element of the queue
if (Q.front == Q.rear)
return ERROR;
e = Q.front -> next -> data;
return OK;
}


int EnQueue(LinkQueue &Q, QElemType e) {
//input an element e as the new rear of the queue
QueuePtr p;
p = (QueuePtr)malloc(sizeof(QNode));
if (!p)
return OVERFLOW;
p -> data = e;
p -> next = NULL;
Q.rear -> next = p;
Q.rear = p;
return OK;
}


int DeQueue(LinkQueue &Q, QElemType &e) {
//delete an element which was the head of the queue
if (Q.front == Q.rear)
return ERROR;
QueuePtr p;
p = Q.front -> next;
e = p -> data;
Q.front -> next = p -> next;
if (Q.rear == p)
Q.rear = Q.front;
free(p);
return OK;
}


int visit(QElemType e) {
cout << e << " ";
return OK;
}


int QueueTraverse(LinkQueue Q, int (*visit)(QElemType)) {
//visit the queue
if (Q.front == NULL)
return ERROR;
QueuePtr p = Q.front -> next;
while(p != NULL) {
if (!visit(p -> data))
return ERROR;
p = p -> next;
}
cout << endl;
return OK;
}


int main() {
LinkQueue q1;
QElemType e;
InitQueue(q1);
cout << QueueEmpty(q1) << endl;
cout << QueueLenth(q1) << endl;
EnQueue(q1, 'a');
EnQueue(q1, 'b');
GetHead(q1, e);
cout << e << endl;
cout << QueueEmpty(q1) << endl;
cout << QueueLenth(q1) << endl;
QueueTraverse(q1, visit);
DeQueue(q1, e);
cout << e << endl;
QueueTraverse(q1, visit);
system("pause");
return 0;
}
附POJ 1637 代码:
#include
#include
#include
#include
#include
using namespace std;


const int MAXN = 1010;
const int MAXM = 50010;
const int INF = 0x3f3f3f3f;




struct Edge
{
int v, f;
int next;
}edge[MAXM];


int n, m;
int cnt;
int s, t;


int first[MAXN], level[MAXN];
int q[MAXN];
int ind[MAXN], outd[MAXN];
int totFlow;


void init()
{
cnt = 0;
totFlow = 0;
memset(first, -1, sizeof(first));
memset(ind, 0, sizeof(ind));
memset(outd, 0, sizeof(outd));
}


void read(int u, int v, int f)
{
edge[cnt].v = v, edge[cnt].f = f;
edge[cnt].next = first[u], first[u] = cnt++;
}


void read_graph(int u, int v, int f)
{
read(u, v, f);
read(v, u, 0);
}


int bfs(int s, int t)
{
memset(level, 0, sizeof(level));
level[s] = 1;
int front = 0, rear = 1;
q[front] = s;
while(front < rear)
{
int x = q[front++];
if(x == t) return 1;
for(int e  = first[x]; e != -1; e = edge[e].next)
{
int v = edge[e].v, f = edge[e].f;
if(!level[v] && f)
{
level[v] = level[x] + 1;
q[rear++] = v;
}
}
}
return 0;
}


int dfs(int u, int maxf, int t)
{
if(u == t) return maxf;
int ret = 0;
for(int e = first[u]; e != -1; e = edge[e].next)
{
int v = edge[e].v, f = edge[e].f;
if(level[v] == level[u] + 1 && f)
{
int Min = min(maxf-ret, f);
f = dfs(v, Min, t);
edge[e].f -= f;
edge[e^1].f += f;
ret += f;
if(ret == maxf) return ret;
}
}
return ret;
}


int Dinic(int s, int t)
{
int ans = 0;
while(bfs(s, t)) ans += dfs(s, INF, t);
return ans;
}


void read_case()
{
init();
scanf("%d%d", &n, &m);
while(m--)
{
int u, v, flag;
scanf("%d%d%d", &u, &v, &flag);
outd[u]++, ind[v]++;
if(u != v)
{
if(!flag) read_graph(u, v, 1);
}
}
}


int build()
{
int flag = 1;
s = 0, t = n+1;
for(int i = 1; i <= n; i++)
{
if((ind[i]+outd[i]) & 1) //出度加入度是奇数 
{
return 0;
}
else if(outd[i] > ind[i]) //出度大于入度 
{
int dif = outd[i]-ind[i];
read_graph(s, i, dif/2);
totFlow += dif/2;

} //可能有入度等于出度的情况,连不连无所谓 
else
{
int dif = ind[i]-outd[i];
read_graph(i, t, dif/2);
}
}
return 1;
}


void solve()
{
read_case();
int flag = build();
int ans = Dinic(s, t);
if(!flag) printf("impossible\n");
else if(ans >= totFlow) printf("possible\n");
else printf("impossible\n"); 
}


int main()
{
int T;
scanf("%d", &T);
while(T--)
{
solve();
}
return 0;
}
附HDU3472代码
/* ***********************************************
Author        :kuangbin
Created Time  :2014-2-3 15:00:40
File Name     :E:\2014ACM\专题学习\图论\欧拉路\混合图\HDU3472.cpp
************************************************ */


#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;


const int MAXN = 30;
//最大流部分
const int MAXM = 10000;
const int INF = 0x3f3f3f3f;
struct Edge
{
    int to,next,cap,flow;
}edge[MAXM];
int tol;
int head[MAXN];
int gap[MAXN], dep[MAXN], pre[MAXN], cur[MAXN];
void init()
{
    tol = 0;
    memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int w,int rw = 0)
{
    edge[tol].to = v;
    edge[tol].cap = w;
    edge[tol].next = head[u];
    edge[tol].flow = 0;
    head[u] = tol++;
    edge[tol].to = u;
    edge[tol].cap = rw;
    edge[tol].next = head[v];
    edge[tol].flow = 0;
    head[v] = tol++;
}
int sap(int start,int end,int N)
{
    memset(gap,0,sizeof(gap));
    memset(dep,0,sizeof(dep));
    memcpy(cur,head,sizeof(head));
    int u = start;
    pre[u] = -1;
    gap[0] = N;
    int ans = 0;
    while(dep[start] < N)
    {
        if(u == end)
        {
            int Min = INF;
            for(int i = pre[u];i != -1;i = pre[edge[i^1].to])
                if(Min > edge[i].cap - edge[i].flow)
                    Min = edge[i].cap - edge[i].flow;
            for(int i = pre[u]; i != -1;i = pre[edge[i^1].to])
            {
                edge[i].flow += Min;
                edge[i^1].flow -= Min;
            }
            u = start;
            ans += Min;
            continue;
        }
        bool flag = false;
        int v;
        for(int i = cur[u]; i != -1;i = edge[i].next)
        {
            v = edge[i].to;
            if(edge[i].cap - edge[i].flow && dep[v] + 1 == dep[u])
            {
                flag = true;
                cur[u] = pre[v] = i;
                break;
            }
        }
        if(flag)
        {
            u = v;
            continue;
        }
        
        int Min = N;
        for(int i = head[u];  i != -1;i = edge[i].next)
            if(edge[i].cap - edge[i].flow && dep[edge[i].to] < Min)
            {
                Min = dep[edge[i].to];
                cur[u] = i;
            }
        gap[dep[u]] --;
        if(!gap[dep[u]])return ans;
        dep[u] = Min+1;
        gap[dep[u]]++;
        if(u != start) u = edge[pre[u]^1].to;
    }
    return ans;
}


int in[30],out[30];
int F[30];
int find(int x)
{
    if(F[x] == -1)return x;
    else return F[x] = find(F[x]);
}
void bing(int u,int v)
{
    int t1 = find(u), t2 = find(v);
    if(t1 != t2)F[t1] = t2;
}
char str[100];
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int T,n;
    scanf("%d",&T);
    int iCase = 0;
    while(T--)
    {
        iCase++;
        scanf("%d",&n);
        memset(F,-1,sizeof(F));
        memset(in,0,sizeof(in));
        memset(out,0,sizeof(out));
        init();
        int k;
        int s = -1;
        while(n--)
        {
            scanf("%s%d",str,&k);
            int len = strlen(str);
            int u = str[0] - 'a';
            int v = str[len-1] - 'a';
            out[u]++;
            in[v]++;
            s = u;
            if(k == 1)
                addedge(u,v,1);
            bing(u,v);
        }
        bool flag = true;
        int cnt = 0;
        int s1 = -1, s2 = -1;
        for(int i = 0;i < 26;i++)
            if(in[i] || out[i])
            {
                if(find(i) != find(s))
                {
                    flag = false;
                    break;
                }
                if((in[i] + out[i])&1)
                {
                    cnt++;
                    if(s1 == -1)s1 = i;
                    else s2 = i;
                }
            }
        if(cnt != 0 && cnt != 2)flag = false;
        if(!flag)
        {
            printf("Case %d: Poor boy!\n",iCase);
            continue;
        }
        if(cnt == 2)
        {
            out[s1]++;
            in[s2]++;
            addedge(s1,s2,1);
        }
        for(int i = 0;i < 26;i++)
        {
            if(out[i] - in[i] > 0)
                addedge(26,i,(out[i] - in[i])/2);
            else if(in[i] - out[i] > 0)
                addedge(i,27,(in[i] - out[i])/2);
        }
        sap(26,27,28);
        for(int i = head[26];i != -1;i = edge[i].next)
            if(edge[i].cap > 0 && edge[i].cap > edge[i].flow)
            {
                flag = false;
                break;
            }
        if(flag)printf("Case %d: Well done!\n",iCase);
        else printf("Case %d: Poor boy!\n",iCase);
    }
    return 0;
}
HDU3018
#include
#include
using namespace std;
const int maxv = 100000 + 2;
int odds[maxv]; /* 顶点 */
int du[maxv]; /* 每个顶点的度数 */
int p[maxv]; /* 并查集数组 */
bool used[maxv];
int scc[maxv]; /* scc个数 */
void init(int n)
{
    for(int i = 0; i <= n; ++i)
    {
        odds[i] = 0;
        p[i] = -1;
        du[i] = 0;
        used[i] = 0;
    }
}
int utf_find(int x)
{
    if(0 <= p[x])
    {
        p[x] = utf_find(p[x]);
        return p[x];
    }
    return x;
}
void utf_union(int a, int b)
{
    int r1 = utf_find(a);
    int r2 = utf_find(b);
    if(r1 == r2)
        return;
    int n1 = p[r1];
    int n2 = p[r2];
    if(n1 < n2)
    {
        p[r2] = r1;
        p[r1] += n2;
    }
    else
    {
        p[r1] = r2;
        p[r2] += n1;
    }
}
int main()
{
    int n = 0;
    int m = 0;
    int a = 0;
    int b = 0;
    int i = 0;
    int cnt = 0;
    while(scanf("%d%d", &n, &m) != EOF)
    {
        init(n);
        cnt = 0;
        for(i = 1; i <= m; ++i)
        {
            scanf("%d%d", &a, &b);
            du[a]++;
            du[b]++;
            utf_union(a, b);
        }
        for(i = 1; i <= n; ++i)
        {
            int f = utf_find(i);
            if(!used[f])
            {
                used[f] = 1;
                scc[cnt++] = f;
            }
            if(1 == du[i]%2)
                odds[f]++;
        }
        int ret = 0;
        for(i = 0; i < cnt; ++i)
        {
            if(0 == du[scc[i]])
                continue;
            if(0 == odds[scc[i]])
                ++ret;
            else
                ret += odds[scc[i]]/2;
        }
        printf("%d\n", ret); 
    }
    return 0;
}
Poj1386
#include
#include
using namespace std;
const int max_len = 1000 + 10;
const int maxv = 27;
int in[maxv]; /* 入度 */
int out[maxv]; /* 出度 */
int p[maxv];/* 并查集数组 */
bool used[maxv];/* 标识字符是否出现在图中 */
void init()
{
    for(int i = 0; i < maxv; ++i)
    {
        in[i] = 0;
        out[i] = 0;
        p[i] = -1;
        used[i] = 0;
    }
}
int find_set(int x)
{
    if(0 <= p[x])
    {
        p[x] = find_set(p[x]);
        return p[x];
    }
    return x;
}
void union_set(int a, int b)
{
    int r1 = find_set(a);
    int r2 = find_set(b);
    if(r1 == r2)
        return;
    int n1 = p[r1];
    int n2 = p[r2];
    if(n1 < n2)
    {
        p[r2] = r1;
        p[r1] += n2;
    }
    else
    {
        p[r1] = r2;
        p[r2] += n1;
    }
}
int main()
{
    int t = 0;
    int n = 0;
    int len = 0;
    int s = 0;
    int e = 0;
    int i = 0;
    char word[max_len];
    scanf("%d", &t);
    while(t--)
    {
        init();
        scanf("%d", &n);
        for(i = 0; i < n; ++i)
        {
            scanf("%s", word);
            len = strlen(word);
            s = word[0] - 'a';
            e = word[len - 1] - 'a';
            used[s] = 1;
            used[e] = 1;
            out[s]++;
            in[e]++;
            union_set(s, e);
        }
        //根据并查集判断图是否连通
        int scc = 0;
        for(i = 0; i < maxv; ++i)
        {
            if(used[i] && 0 > p[i])
                ++scc;
        }
        if(1 < scc)
        {
            printf("The door cannot be opened.\n");
            continue;
        }
        //入度是否等于出度
        int a = 0;
        int b = 0;
        for(i = 0; i < maxv; ++i)
        {
            if(used[i] && in[i] != out[i])
            {
                if(1 == (in[i] - out[i]))
                    ++a;
                else if(1 == (out[i] - in[i]))
                    ++b;
                else
                    break;
            }
        }
        if(i < maxv)
            printf("The door cannot be opened.\n");
        else if(0 == (a + b) || (1 == a && 1 == b))
            printf("Ordering is possible.\n");
        else 
            printf("The door cannot be opened.\n");
    }
    return 0;
}
POJ2230
#include
#include
using namespace std;
const int maxm = 2*50000 + 1;
const int maxv = 10000 + 5;
struct edge{
    int to;
    int next;
};
edge node[maxm]; /*邻接表*/
int adj[maxv];
bool used[maxm];/* 标记边是否访问过*/
void Euler(int vertix)
{
    for(int i = adj[vertix]; i != -1; i = node[i].next)
    {
        if(!used[i])
        {
            used[i] = 1;
            Euler(node[i].to);
        }
    }
    printf("%d\n", vertix);
}
int main()
{
    int n = 0;
    int m = 0;
    int i = 0;
    int u = 0;
    int v = 0;
    int cnt = 0;
    scanf("%d%d", &n, &m);
    for(i = 0; i <= n; ++i)
        adj[i] = -1;
    for(i = 0; i <= m*2; ++i)
        used[i] = 0;
    for(i = 0; i < m; ++i)
    {
        scanf("%d%d", &u, &v);
        //u->v
        node[cnt].to = v;
        node[cnt].next = adj[u];
        adj[u] = cnt++;
        //v->u
        node[cnt].to = u;
        node[cnt].next = adj[v];
        adj[v] = cnt++;
    }
    Euler(1);
    return 0;
}
Poj2337
#include
#include
using namespace std;
const int maxn = 1000 + 10;
const int max_l = 28;
//char word[maxn][max_l];
char ret[maxn][max_l];
struct edge{
    int to;
    int next;
    char str[max_l];
};
edge node[maxn];
int adj[max_l];
int in[max_l];
int out[max_l];
bool used[maxn];
bool exist[max_l]; 
int p[max_l]; /* 并查集 */
int num_e = 0;
int ret_e = 0;
//从大到小排序
bool cmp(edge p1, edge p2)
{
    return strcmp(p1.str, p2.str) > 0;
}
void init()
{
    for(int i = 0; i < max_l; ++i)
    {
        in[i] = 0;
        out[i] = 0;
        p[i] = -1;
        exist[i] = 0;
        adj[i] = -1;
    }
    for(int j = 0; j < maxn; ++j)
        used[j] = 0;
    num_e = 0;
    ret_e = 0;
}
int find_set(int u)
{
    if(0 <= p[u])
    {
        p[u] = find_set(p[u]);
        return p[u];
    }
    return u;
}
void union_set(int u, int v)
{
    int r1 = find_set(u);
    int r2 = find_set(v);
    if(r1 == r2)
        return;
    int n1 = p[r1];
    int n2 = p[r2];
    if(n1 < n2)
    {
        p[r2] = r1;
        p[r1] += n2;
    }
    else
    {
        p[r1] = r2;
        p[r2] += n1;
    }
}
void Eular(int vertix, int idx)
{
    for(int i = adj[vertix]; i != -1; i = node[i].next)
    {
        if(!used[i])
        {
    //        strcpy(ret[ret_e++], node[i].str);
            used[i] = 1;
            Eular(node[i].to, i);
        }
    }
    /*idx就是node数组的下标,标识一条边*/
    if(0 <= idx)
        strcpy(ret[ret_e++], node[idx].str);
}
int main()
{
    int t = 0;
    int n = 0;
    int i = 0;
    int u = 0;
    int v = 0;
    int start = 0; //从哪个顶点开始遍历
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d", &n);
        if(!n)
            continue;
        for(i = 0; i < n; ++i)
            scanf("%s", node[i].str);
        init();
        start = max_l;
        sort(node, node + n, cmp);
        //建图
        for(i = 0; i < n; ++i)
        {
            u = node[i].str[0] - 'a';
            v = node[i].str[strlen(node[i].str) - 1] - 'a';
            in[v]++;
            out[u]++;
            exist[u] = 1;
            exist[v] = 1;
            union_set(u, v);
            node[num_e].to = v;
            node[num_e].next = adj[u];
            adj[u] = num_e++;
        }
        //判断是否连通
        int scc = 0;
        for(i = 0; i < max_l; ++i)
        {
            if(exist[i] && 0 > p[i])
                ++scc;
            if(1 < scc)
                break;
        }
        if(1 < scc) //不连通
        {
            printf("***\n");
            continue;
        }
        //是通路or回路
        int a = 0;
        int b = 0;
        start = -1;
        for(i = 0; i < max_l; ++i)
        {
            if(exist[i] && in[i] != out[i])
            {
                if(1 == in[i] - out[i])
                    ++a;    
                else if(1 == out[i] - in[i])
                {
                    ++b;
                    start = i;
                }
                else
                    break;
            }
        }
        if(i < max_l)
        {
            printf("***\n");
            continue;
        }
        else
        {
            if(!((0 == a + b) || (1 == a && 1 == b)))
            {
                printf("***\n");
                continue;
            }
            if(-1 == start)
            {//回路 找到第一个存在的字母
                int k = 0;
                for(k = 0; k < max_l; ++k)
                {
                    if(out[k])
                        break;
                }
                start = k;
            }
            //从顶点start开始dfs
            Eular(start, -1);
            printf("%s", ret[ret_e - 1]);
            for(i = ret_e - 2; i >= 0; --i)
                printf(".%s", ret[i]);
            printf("\n");
        }
    }
    return 0;
}
HDU1878
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;


#define Maxn 1005
int n,m;
int g[Maxn][Maxn];
int deg[Maxn];
int vis[Maxn];


bool progress()
{
    for(int i=1;i<=n;i++)
    {
        if(deg[i]&1) return false;
    }
    return true;
}
bool bfs(int s)
{
    queue q;
    memset(vis,0,sizeof(vis));
    q.push(s);
    vis[s] = 1;
    while(!q.empty())
    {
        int temp = q.front();
        q.pop();
        for(int i=1;i<=n;i++)
        {
            if(g[temp][i] && !vis[i]) vis[i] = 1,q.push(i);
        }
    }
    for(int i=1;i<=n;i++)
    {
        if(vis[i] == 0) return false;
    }
    return true;
}
int main()
{
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
    #endif
    int a,b;
    while(scanf(" %d",&n)!=EOF)
    {
        if(n==0) break;
        memset(g,0,sizeof(g));
        memset(deg,0,sizeof(deg));
        scanf(" %d",&m);
        for(int i=0;i         {
            scanf(" %d %d",&a,&b);
            g[a][b] = g[b][a] = 1;
            deg[a]++,deg[b]++;
        }
        if(!progress()) puts("0");
        else if(bfs(1)) puts("1");
        else puts("0");
    }
    return 0;
}


POJ 1041
#include
#include
#include
#include
#include
#include
using namespace std;


#define Maxn 100
#define Maxm (2000<<2)


vector > g[Maxn];
int vis[Maxm];


int father[Maxn];
int deg[Maxn];
int path[Maxm];
int top = 0;


void init()
{
for(int i=0;i{
g[i].clear();
father[i] = i;
}
memset(deg,0,sizeof(deg));
}


void addEdge(int a,int b,int z)
{
g[a].push_back(make_pair(b,z));
g[b].push_back(make_pair(a,z));
}


int findset(int i)
{
return i == father[i] ? i : father[i] = findset(father[i]);
}
void merge(int a,int b)
{
a = findset(a),b = findset(b);
if(a!=b)
{
father[a] = b;
}
}
bool cmp(pair a,pair b)
{
return a.second < b.second;
}
bool judge()
{
//是否连通
for(int i=0;i{
sort(g[i].begin(),g[i].end(),cmp);
for(int j=0;j{
if(findset(i) != findset(g[i][j].first))
{
return false;
}
}
}
//判断每个点的度是否是偶数
for(int i=0;i{
if(deg[i]!=0 && deg[i]&1 == 1) return false;
}
return true;
}
void euler(int s)
{
for(int i=0;i{
if(!vis[g[s][i].second])
{
vis[g[s][i].second] = 1;
euler(g[s][i].first);
//路径节点的位置在dfs之后
path[top++] = g[s][i].second;
}
}
}
void output()
{
for(int i=top-1;i>=0;i--)
{
if(i == top-1) printf("%d",path[i]);
else printf(" %d",path[i]);
}
puts("");
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
int a,b,z;
int s;
while(scanf(" %d %d",&a,&b)!=EOF)
{
if(a == 0 && b==0) break;
init();
s = min(a,b);
scanf(" %d",&z);
addEdge(a,b,z);
merge(a,b);
deg[a]++,deg[b]++;
while(scanf(" %d %d",&a,&b)!=EOF)
{
if(a == 0 && b==0) break;
scanf(" %d",&z);
addEdge(a,b,z);
merge(a,b);
deg[a]++,deg[b]++;
}
if(judge())
{
memset(vis,0,sizeof(vis));
top = 0;
euler(s);
output();
}
else printf("Round trip does not exist.\n");
}
return 0;
}
UVA10054
#include
#include
#include
#include
#include
#include
using namespace std;


#define Maxn 60
#define Maxm 2000




struct Edge
{
int u,v;
int id;
};
vector g[Maxn];
int edge_num,top;
int father[Maxn],deg[Maxn],used[Maxn];
Edge path[Maxm];
bool vis[Maxm];


void init()
{
for(int i=0;i{
g[i].clear();
father[i] = i;
deg[i] = 0,used[i] = 0;
}
memset(vis,false,sizeof(vis));
edge_num = 0;
top = 0;
}
int findset(int i)
{
return i==father[i] ? i : father[i] = findset(father[i]);
}
void merge(int a,int b)
{
a = findset(a),b = findset(b);
if(a!=b) father[a] = b;
}
void addEdge(int a,int b)
{
Edge tmp;
tmp.u = a,tmp.v = b,vis[edge_num] = false,tmp.id = edge_num++;
g[a].push_back(tmp);
tmp.u = b,tmp.v = a,vis[edge_num] = false,tmp.id = edge_num++;
g[b].push_back(tmp);
}
bool judge()
{
//degree can not be odd
for(int i=0;i{
if(deg[i]&1) return false;
}
//must be connected
int cnt = 0;
for(int i=0;i{
if(used[i] && i == father[i]) cnt++;
}
return cnt == 1;
}
void output()
{
for(int i=top-1;i>=0;i--)
{
printf("%d %d\n",path[i].u,path[i].v);
}
}
void euler(int s)
{
for(int i=0;i{
if(!vis[g[s][i].id])
{
vis[g[s][i].id] = vis[(g[s][i].id)^1] = 1;
euler(g[s][i].v);
path[top++] = g[s][i];
}
}
}
int s;
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
int t;
int n;
int a,b,cas;
cas = 0;
scanf(" %d",&t);
while(t--)
{
cas++;
if(cas!=1) puts("");
printf("Case #%d\n",cas);
init();
scanf(" %d",&n);
for(int i=0;i{
scanf(" %d %d",&a,&b);
addEdge(a,b);
merge(a,b);
deg[a]++,deg[b]++;
used[a] = 1,used[b] = 1;
s = a;
}
if(judge()) euler(s),output();
else puts("some beads may be lost");
}
return 0;
}
POJ2762
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;


#define Maxn 1005
#define Maxm 10005


int first[Maxn],next[Maxm];
int tot,tot2;


struct Edge
{
    int a,b;
}edge[Maxm],edge2[Maxm];


//scc相关
int dfn[Maxn],low[Maxn],scccno[Maxn],dfs_clock,scc_cnt;
bool instack[Maxn];
stack st;


//缩点相关
int first2[Maxn],next2[Maxm],indeg[Maxn];


void init()
{
    memset(first,-1,sizeof(first));
    tot = 0;
}
void addEdge(int a,int b)
{
    edge[tot].a = a,edge[tot].b = b;
    next[tot] = first[a];
    first[a] = tot++;
}
void addEdge2(int a,int b)
{
    edge2[tot2].a = a,edge2[tot2].b = b;
    next2[tot2] = first2[a];
    first2[a] = tot2++; 
}
void tarjan(int u)
{
    dfn[u] = low[u] = ++dfs_clock;
    st.push(u);
    instack[u] = true;
    for(int i=first[u];i!=-1;i=next[i])
    {
        int v = edge[i].b;
        if(!dfn[v])
        {
            tarjan(v);
            low[u] = min(low[u],low[v]);
        }
        else if(instack[v])
        {
            low[u] = min(low[u],dfn[v]); 
        }
    }
    if(low[u] == dfn[u])
    {
        scc_cnt++;
        while(1)
        {
            int v = st.top();
            st.pop();
            instack[v] = false;
            scccno[v] = scc_cnt;
            if(u == v) break;
        }
    }
}
void find_scc(int n)
{
    dfs_clock = scc_cnt = 0;
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(instack,false,sizeof(instack));
    for(int i=1;i<=n;i++)
    {
        if(!dfn[i]) tarjan(i);
    }
}
void getDag(int n)
{
    memset(first2,-1,sizeof(first2));
    memset(indeg,0,sizeof(indeg));
    tot2 = 0;
    for(int i=1;i<=n;i++)
    {
        for(int v=first[i];v!=-1;v=next[v])
        {
            int j = edge[v].b;
            if(scccno[i]!=scccno[j])
            {
                addEdge2(scccno[i],scccno[j]);
                indeg[scccno[j]]++;
            }
        }
    }
}


bool toposort(int n)
{
    queue q;
    for(int i=1;i<=n;i++)
    {
        if(indeg[i] == 0) q.push(i);
    }
    while(!q.empty())
    {
        int temp = q.front();
        q.pop();
        if(!q.empty()) return false;
        for(int i=first2[temp];i!=-1;i=next2[i])
        {
            int v = edge2[i].b;
            indeg[v]--;
            if(indeg[v] == 0) q.push(v);
        }
    }
    return true;
}
int main()
{
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
    #endif
    int n,m;
    int t;
    int a,b;
    scanf(" %d",&t);
    while(t--)
    {
        init();
        scanf(" %d %d",&n,&m);
        for(int i=0;i         {
            scanf(" %d %d",&a,&b);
            addEdge(a,b);
        }
        find_scc(n);
        getDag(n);
        if(toposort(scc_cnt)) puts("Yes");
        else puts("No");
    }
    return 0;
}


你可能感兴趣的:(算法学习笔记)