欧拉路径-欧拉回路

有向图定理:有向图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;
}











  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值