UVA 10735 Euler Circuit

题意:求混合图的欧拉路径。

这题的困难之处在于无向边只能用一次,相当于一个方向未定的有向边。

首先用并查集判断图的连通性,(直接计数O(1),做1395 Slim Span学到的技巧)。

我们知道有向图的欧拉路径存在的充要条件是最多两个点的入度不等于出度,而且相差为1,这题不需要考虑这种情况,只需要所有点的入度等于出度就行了。

对于无向边,一开始可以随意确定一个方向。这样不能保证所有点的入度等于出度,但是可以想办法调整。

比如说u->v,那么如果改变方向的话,就相当于把一个出度运输给了v。

这让人联想到了网络流,一条无向边就对应着一条容量为1的边,建图的时候就把u在v直接连一条容量为1的边。

那么剩下的就是决定怎么运输?运输多少?我们的目标是调整使得所有的点入度等于出度。

因为每次调整入度和出度的差改变都为偶,那么如果有入度和出度相差为奇数的点,那么一定是不满足条件的。

下面只考虑度数差为偶的点,对于入度大于出度的点,那么这个点需要出度,需要(in[u]-out[u])/2个出度,那么就把它和汇点连一条相应容量的边。

对于出度大的点类似处理。跑网络流,进行调整。根据残流网络,可以得出边的指向。在跑Euler路径就行了。

#include<bits/stdc++.h>
using namespace std;

int V,E;
const int maxv = 105;
#define PB push_back

struct Node
{
    int v;
    bool vis;
};

vector<Node> D[maxv];

int in[maxv],out[maxv];

struct Edge
{
    int v,cap;
};

vector<Edge> edges;
vector<int> G[maxv];
int S,T;
int vcnt;
void AddEdge(int u,int v,int c)
{
    G[u].PB(edges.size());
    edges.PB({v,c});
    G[v].PB(edges.size());
    edges.PB({u,0});
}

int lv[maxv];
int q[maxv];

bool bfs()
{
    memset(lv,0,sizeof(int)*(vcnt));
    int l = 0, r = 0;
    q[r++] = S; lv[S] = 1;
    while(r>l){
        int u = q[l++];
        for(int i = 0; i < G[u].size(); i++){
            Edge &e = edges[G[u][i]];
            if(!lv[e.v] && e.cap){
                lv[e.v] = lv[u]+1;
                q[r++] = e.v;
            }
        }
    }
    return lv[T];
}

int cur[maxv];
int dfs(int u,int a)
{
    if(u == T||!a) return a;
    int flow = 0,f;
    for(int &i = cur[u]; i < G[u].size(); i++){
        Edge &e = edges[G[u][i]];
        if(lv[e.v] == lv[u]+1 && (f = dfs(e.v,min(e.cap,a)))){
            flow += f;
            a -= f;
            e.cap -= f;
            edges[G[u][i]^1].cap+=f;
            if(!a) break;
        }
    }
    return flow;
}

const int INF = 0x3f3f3f3f;
int MaxFlow()
{
    int flow = 0;
    while(bfs()){
        memset(cur,0,sizeof(int)*(vcnt));
        flow += dfs(S,INF);
    }
    return flow;
}

//返回需要的总流量
int build()
{
    int del = 0;
    for(int i = 1; i <= V; i++){
        int d;
        if((in[i]+out[i])&1) return -1;
        if(out[i]<in[i]) {
            d = (in[i]-out[i])>>1; del+=d; AddEdge(i,T,d);
        }else if(in[i]<out[i]){
            d = (out[i]-in[i])>>1;
            AddEdge(S,i,d);
        }
    }
    return del;
}

int pa[maxv];
int Find(int x) { return x == pa[x]?x:pa[x]=Find(pa[x]); }
bool vis[maxv];
int cnt;

void init()
{
    S = 0; T = V+1; vcnt = T+1;
    for(int i = 1; i <= V; i++) D[i].clear(),in[i]=out[i]=0;
    edges.clear();
    for(int i = 0; i <= T; i++) G[i].clear();
    for(int i = 1; i <= V; i++) pa[i] = i;
    memset(vis,0,sizeof(bool)*vcnt);
    cnt = 0;
}

bool read()
{
    scanf("%d%d",&V,&E);
    init();
    for(int i = 0; i < E; i++){
        char ch;
        int u,v; scanf("%d %d %c",&u,&v,&ch);
        if(ch == ‘U‘){
            AddEdge(u,v,1); //u可以把一个单位的出度运输给v
        }else {
            D[u].PB({v,false});
        }
        if(!vis[u]) cnt++,vis[u] = true;
        if(!vis[v]) cnt++,vis[v] = true;
        int s1 = Find(u),s2 = Find(v);
        if(s1 != s2) {
            cnt--;
            pa[s1] = s2;
        }
        out[u]++; in[v]++;
    }
    return cnt == 1;
}

void reBuild()
{
    for(int u = 1; u <= V; u++){
        for(int i = 0; i < G[u].size(); i++){
            Edge &e = edges[G[u][i]];
            if(e.cap) {
                int v0 = edges[G[u][i]^1].v, v1 = e.v;
                if(v0&&v0<=V&&v1&&v1<=V) D[v0].PB({v1,false});
            }
        }
    }
}

stack<int> ans;
void Euler(int u)
{
    for(int i = 0; i < D[u].size(); i++){
        if(D[u][i].vis) continue;
        D[u][i].vis = true;
        int v = D[u][i].v;
        Euler(v);
        ans.push(v);
    }
}

void solve()
{
    if(read()) {
        int totFlow = build();
        if(~totFlow  && totFlow <= MaxFlow()) {
            reBuild();
            memset(cur,0,sizeof(int)*(vcnt));
            Euler(1);
            printf("1");
            while(ans.size()){
                printf(" %d",ans.top());
                ans.pop();
            }
            putchar(‘\n‘);
            return;
        }
    }
    puts("No euler circuit exist");
}

int main()
{
    //freopen("in.txt","r",stdin);
    int Test; scanf("%d",&Test);

    while(Test--){
        solve();
        if(Test) putchar(‘\n‘);
    }
    return 0;
}

PS:

欧拉路径输出的套圈算法,进行了一点小小的优化,从dinic中收到启发改成了用数组cur[],这样可以节省判断vis的时间。

还有一个问题,此题是SJ,如下直接在dfs里逆序输出就WA。。。保存答案stack里输出才A,这样写有什么trick的情况吗?

void Euler(int u)
{
    for(int &i = cur[u]; i < D[u].size();){
        int v = D[u][i++];
        Euler(v);
        printf("%d ",v);
    }
}

调用
            memset(cur,0,sizeof(int)*(vcnt));
            Euler(1);
            printf("1\n");
时间: 2024-10-27 07:47:39

UVA 10735 Euler Circuit的相关文章

UVa 10735 - Euler Circuit(最大流 + 欧拉回路)

链接: https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1676 题意: 给出一个V个点和E条边(1≤V≤100,1≤E≤500)的混合图(即有的边是无向边,有的边是有向边),试求出它的一条欧拉回路,如果没有,输出无解信息.输入保证在忽略边的方向之后图是连通的. 分析: 很多混合图问题(例如,混合图的最短路)都可以转化为有向图问题,方法是把

UVA 10735 Euler Circuit 混合图的欧拉回路(最大流,fluery算法)

题意:给一个图,图中有部分是向边,部分是无向边,要求判断是否存在欧拉回路,若存在,输出路径. 分析:欧拉回路的定义是,从某个点出发,每条边经过一次之后恰好回到出发点. 无向边同样只能走一次,只是不限制方向而已,那么这个情况下就不能拆边.不妨先按照所给的start和end的顺序,初步定下该无向边的顺序(若不当,一会再改).那么有个问题,我们需要先判断其是否存在欧拉回路先. 混合图不满足欧拉回路因素有:(1)一个点的度(无论有无向)是奇数的,那么其肯定不能满足出边数等于入边数.(2)有向边的出入度过

UVa 10735 (混合图的欧拉回路) Euler Circuit

题意: 给出一个图,有的边是有向边,有的是无向边.试找出一条欧拉回路. 分析: 按照往常的思维,遇到混合图,我们一般会把无向边拆成两条方向相反的有向边. 但是在这里却行不通了,因为拆成两条有向边的话,就表示这个边能“在两个相反方向各经过一次”. 而题意是这个边只能经过一次. 假设图中存在欧拉回路,则所有点的出度out(i) 等于 入度in(i) 不妨这样,先将所有的无向边任意定向,对于out(u) > in(u)的点,可以将已经定向的无向边u->v反向为v->u,这样out(u) - i

UVA LIVE-3263 - That Nice Euler Circuit

画一个顶点为偶数的封闭的二维图,当然.这个图能够自交,给出画的过程中的一些轨迹点.求出这个图把二次元分成了几部分,比如三角形把二次元分成了两部分. 这个的话,有图中顶点数+部分数-棱数=2的定律,这是核心思想.也就是所谓的欧拉定律拓扑版,好吧,事实上细致想想也是可以想出这个规律来的. 做出这题纯属意外,因为给的点的坐标全是用整数表示,为了不用考虑精度问题,一開始.我就想仅仅用这些点.就是说不再算出其他交点之类的,就把答案算出, 由于当前轨迹与之前轨迹无非三种情况:规范与不规范相交,不相交 不相交

【UVA】1342 - That Nice Euler Circuit(几何+欧拉定理)

E 为边数 ,V 为点数,F为面数 那么 F = E + 2 - V(其中包括了一个无限大的面) 这道题被自己的习惯坑了一下#define MAXD 300 + 10 和#define MAXD 310 是不一样的 14113235 1342 That Nice Euler Circuit Accepted C++ 0.082 2014-08-29 15:12:20 自己的代码: #include<cstdio> #include<cstring> #include<iost

poj2284 That Nice Euler Circuit(欧拉公式)

题目链接:poj2284 That Nice Euler Circuit 欧拉公式:如果G是一个阶为n,边数为m且含有r个区域的连通平面图,则有恒等式:n-m+r=2. 欧拉公式的推广: 对于具有k(k≥2)个连通分支的平面图G,有:n-m+r=k+1. 题意:给出连通平面图的各顶点,求这个欧拉回路将平面分成多少区域. 题解:根据平面图的欧拉定理“n-m+r=2”来求解区域数r. 顶点个数n:两两线段求交点,每个交点都是图中的顶点. 边数m:在求交点时判断每个交点落在第几条边上,如果一个交点落在

UVA 10735 混合图的欧拉回路+输出路径

#include<cstdio> #include<cstring> #include<cmath> #include<iostream> #include<algorithm> #include<set> #include<map> #include<queue> #include<vector> #include<string> #define eps 1e-12 #define I

UVALive - 3263 That Nice Euler Circuit (几何)

UVALive - 3263 That Nice Euler Circuit (几何) ACM 题目地址: UVALive - 3263 That Nice Euler Circuit 题意: 给出一个点,问连起来后的图形把平面分为几个区域. 分析: 欧拉定理有:设平面图的顶点数.边数.面数分别V,E,F则V+F-E=2 大白的题目,做起来还是很有技巧的. 代码: /* * Author: illuz <iilluzen[at]gmail.com> * File: LA3263.cpp * C

POJ2284 That Nice Euler Circuit (欧拉公式)(计算几何 线段相交问题)

That Nice Euler Circuit Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 1977   Accepted: 626 Description Little Joey invented a scrabble machine that he called Euler, after the great mathematician. In his primary school Joey heard about