有向图定理:有向图D存在欧拉通路的充要条件是:
D为有向图,D的基图(就是图D对应的无向图)连通,并且每个节点的出度等于它的入度;
<
或者>
除两个节点外,其他节点的出度与入度都相等,而这两个节点中,其中一个节点的出度与入度的差为1(out-in=1),另一个顶点的出度与入度之差为-1(out-in=-1)
判断欧拉路是否存在的方法:
有向图:图连通,有一个顶点出度大入度1,有一个顶点入度大出度1,其余都是出度=入度。
无向图:图连通,只有两个顶点是奇数度,其余都是偶数度的。
判断欧拉回路是否存在的方法:
有向图:图连通,所有的顶点出度=入度。
无向图:图连通,所有顶点都是偶数度。
欧拉回路
每条边只经过一次,而且回到起点
无向图:
连通(不考虑度为0的点),每个顶点度数都为偶数。
/*
* SGU 101
*/
struct Edge
{
int to;
int next;
int index;
int dir;
bool flag;
} edge[220];
int head[10]; //前驱
int tot;
void init()
{
memset(head, -1, sizeof((head)));
tot = 0;
}
void addEdge(int u, int v, int index)
{
edge[tot].to = v;
edge[tot].next = head[u];
edge[tot].index = index;
edge[tot].dir = 0;
edge[tot].flag = false;
head[u] = tot++;
edge[tot].to = u;
edge[tot].next = head[v];
edge[tot].index = index;
edge[tot].dir = 1;
edge[tot].flag = false;
head[v] = tot++;
return ;
}
int du[10];
vector<int>ans;
void dfs(int u)
{
for (int i = head[u]; i != -1; i = edge[i].next)
{
if (!edge[i].flag)
{
edge[i].flag = true;
edge[i ^ 1].flag = true;
dfs(edge[i].to);
ans.push_back(i); //容器尾部插入i
}
}
return ;
}
int main()
{
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
int n;
while (cin >> n)
{
init();
int u, v;
memset(du, 0, sizeof(du));
for (int i = 1; i <= n; i++)
{
cin >> u >> v;
addEdge(u, v, i);
du[u]++;
du[v]++;
}
int s = -1;
int cnt = 0;
for (int i = 0; i <= 6; i++)
{
if (du[i] & 1)
{
cnt++;
s = i;
}
if (du[i] > 0 && s == -1)
{
s = i;
}
}
if (cnt != 0 && cnt != 2)
{
cout << "No solution" << endl;
continue;
}
ans.clear();
dfs(s);
if (ans.size() != n)
{
cout << "No solution" << endl;
continue;
}
for (int i = 0; i < ans.size(); i++)
{
printf("%d ", edge[ans[i]].index);
if (edge[ans[i]].dir == 0)
{
cout << "-" << endl;
}
else
{
cout << "+" << endl;
}
}
}
return 0;
}
有向图:
基图连通(把边当成无向边,同样不考虑度为0的点),每个顶点出度等于入度。
欧拉路径
每条边只经过一次,不要求回到起点
无向图:
连通(不考虑度为0的点),每个顶点度数都为偶数或者仅有两个点的度数为奇数。
/*
* O(E)
* INIT:adj[][]置为图的邻接表;cnt[a]为a点的邻接点数
* CALL:alpath(0); 注意:不要有自向边
*/
const int V = 10000;
int adj[V][V];
int idx[V][V];
int cnt[V];
int stk[V];
int top = 0;
int path(int v)
{
for (int w; cnt[v] > 0; v = w)
{
stk[top++] = v;
w = adj[v][--cnt[v]];
adj[w][idx[w][v]] = adj[w][--cnt[w]];
//处理的是无向图——边是双向边,删除v->w后,还要处理删除w->v
}
return v;
}
void elpath(int b, int n)
{
int i, j;
for (i = 0; i < n; i++)
{
for (j = 0; j < cnt[i]; j++)
{
idx[i][adj[i][j]] = j;
}
}
cout << b;
for (top = 0; path(b) == b && top != 0; )
{
b = stk[--top];
cout << '-' << b;
}
cout << endl;
}
有向图:
基图连通(把边当成无向边,同样不考虑度为0的点),每个顶点出度等于入度或者有且仅有一个点的出度比入度多1,有且仅有一个点的出度比入度少1,其余的出度等于入度。
/*
* POJ 2337
* 给出n个小写字母组成的单词,要求将n个单词连接起来。使得前一个单词的最后一个字母和
* 后一个单词的第一个字母相同。输出字典序最小解
*/
struct Edge
{
int to;
int next;
int index;
bool flag;
}edge[2010];
int head[30];
int tot;
void init()
{
tot = 0;
memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, int index)
{
edge[tot].to = v;
edge[tot].next = head[u];
edge[tot].index = index;
edge[tot].flag = false;
head[u] = tot++;
return ;
}
string str[1010];
int in[30];
int out[30];
int cnt;
int ans[1010];
void dfs(int u)
{
for (int i = head[u]; i != -1; i = edge[i].next)
{
if (!edge[i].flag)
{
edge[i].flag = true;
dfs(edge[i].to);
ans[cnt++] = edge[i].index;
}
}
return ;
}
int main()
{
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
int T, n;
cin >> T;
while (T--)
{
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> str[i];
}
sort(str, str + n); //要输出字典序最小的解,先按照字典序排序
init();
memset(in, 0, sizeof(in));
memset(out, 0, sizeof(out));
int start = 100;
for (int i = n - 1; i >= 0; i--) //字典序大的先加入
{
int u = str[i][0] - 'a';
int v = str[i][str[i].length() - 1] - 'a';
addEdge(u, v, i);
out[u]++;
in[v]++;
if (u < start)
{
start = u;
}
if (v < start)
{
start = v;
}
}
int ccOne = 0;
int ccTwo = 0;
for (int i = 0; i < 26; i++)
{
if (out[i] - in[i] == 1)
{
ccOne++;
start = i; //如果有一个出度比入度大1的点,就从这个点出发,否则从最小的点出发
}
else if (out[i] - in[i] == -1)
{
ccTwo++;
}
else if (out[i] - in[i] != 0)
{
ccOne = 3;
}
}
if (!((ccOne == 0 && ccTwo == 0) || (ccOne == 1 && ccTwo == 1)))
{
cout << "***" << endl;
continue;
}
cnt = 0;
dfs(start);
if (cnt != n) //判断是否连通
{
cout << "***" << endl;
continue;
}
for (int i = cnt - 1; i >= 0; i--)
{
cout << str[ans[i]];
if (i > 0)
{
cout << '.';
}
else
{
cout << endl;
}
}
}
return 0;
}
混合图:
如果存在欧拉回路,一定存在欧拉路径,否则如果有且仅有两个点的(出度-入度)是奇数,那么给这两个点加边,判断是否存在欧拉回路,如果存在就一定存在欧拉路径。
/*
* POJ 1637
* 本题保证了连通,故不需要判断连通,否则要判断连通
*/
const int MAXN = 210;
const int MAXM = 20100; //最大流ISAP部分
const int INF = 0x3f3f3f3f;
struct Edge
{
int to;
int next;
int cap;
int flow;
}edge[MAXM];
int tol;
int head[MAXN];
int gap[MAXN];
int dep[MAXN];
int pre[MAXN];
int cur[MAXN];
void init()
{
tol = 0;
memset(head, -1, sizeof(head));
return ;
}
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++;
return ;
}
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 = 0;
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;
}
//the end of 最大流部分
int in[MAXN];
int out[MAXN];
int main()
{
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
int T;
int n, m;
cin >> T;
while (T--)
{
cin >> n >> m;
init();
int u, v, w;
memset(in, 0, sizeof(in));
memset(out, 0, sizeof(out));
while (m--)
{
cin >> u >> v >> w;
out[u]++;
in[v]++;
if (w == 0)
{
addEdge(u, v, 1); //双向
}
}
bool flag = true;
for (int i = 1; i <= n; i++)
{
if (out[i] - in[i] > 0)
{
addEdge(0, i, (out[i] - in[i]) / 2);
}
else if (in[i] - out[i] > 0)
{
addEdge(i, n + 1, (in[i] - out[i]) / 2);
}
if ((out[i] - in[i]) & 1)
{
flag = false;
}
}
if (!flag)
{
cout << "impossible" << endl;
continue;
}
sap(0, n + 1, n + 2);
for (int i = head[0]; i != -1; i = edge[i].next)
{
if (edge[i].cap > 0 && edge[i].cap > edge[i].flow)
{
flag = false;
break;
}
}
if (flag)
{
cout << "possible" << endl;
}
else
{
cout << "impossible" << endl;
}
}
return 0;
}