0925考试T2 欧拉回路

0925考试T2

​ 题目大意:

​ 给定一个任意的无向图,问最少画几笔,使得所有的边被画过一次且仅一次。

​ 欧拉回路。

​ 我们称有偶数条边连着的点叫偶数点,有奇数条边连着的点叫奇数点。

​ 欧拉路:有一条路径可以经过所有边并且不重复。欧拉回路:闭合的欧拉路。

​ 首先我们要知道:无向图中存在欧拉回路的条件是所有点都是偶数点,因为有进就有出嘛。

​ 对于这个题,有好多个联通块,每个联通块是一个无向图,每个联通块的答案是独立的。对于某个无向图既有奇数点又有偶数点,我们可以把所有奇数点变为偶数点。把奇数点变为偶数点肯定要加边,我们把这些边标记一下,相当于在整个欧拉回路里是一个断点,就是由这些新加的边确定到底要画几笔。

​ 那具体怎么加呢?我们让奇数点之间两两连边,至少要加\(max(\frac{k}{2}, 1)\)条边,\(k\)是奇数点的个数,因为每加一条边就会消去两个奇数点。把这些加的边叫做虚边。

​ 我们在\(dfs\)的时候,遇到一条边就标记一条,表示下次不会搜这条边了。如果碰到一个虚边,我们就让次数加一。

#include <bits/stdc++.h>

using namespace std;

inline double read() {
    double s = 0, f = 1; char ch;
    while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
    for(s = ch ^ 48;isdigit(ch = getchar()); s = (s * 10) + (ch ^ 48));
    return s * f;
}

const int N = 1e5 + 5;
int T, n, m, cnt, tot;
int vis[N], out[N], head[N];
vector <int> a[N];
struct edge { int f, to, id, nxt; } e[N << 3];

void clear() {
    cnt = 1, tot = 0; //cnt = 1为了成对变换
    for(int i = 0;i <= n; i++) head[i] = vis[i] = out[i] = 0;
}

void add(int x, int y, int id) {
    e[++cnt].nxt = head[x]; head[x] = cnt; e[cnt].to = y; e[cnt].id = id; e[cnt].f = 0;
}

void dfs(int x) {
    vis[x] = 1;
    for(int i = head[x]; i ; i = e[i].nxt) {
        int y = e[i].to; int id = e[i].id;
        if(!e[i].f) {
            e[i].f = e[i ^ 1].f = 1; //将这条边和反向边标记
            dfs(y);
            if(id) a[tot].push_back(-id); //-id是为了输出顺序是对的
            else tot ++; //扫到虚边
        }
    }
}

int main() {

    T = read(); 
    while(T --> 0) {
        n = read(); m = read(); clear();
        for(int i = 1, x, y;i <= m; i++) {
            x = read(); y = read(); out[x] ++; out[y] ++;
            add(x, y, i); add(y, x, -i);
        }

        int last = 0;
        for(int i = 1;i <= n; i++) 
            if(out[i] & 1) 
                if(last) {
                    add(last, i, 0); add(i, last, 0); last = 0;
                } //奇数点两两连边
                else last = i;
        
        for(int i = 1;i <= n; i++) 
            if(!vis[i] && (out[i] & 1)) {
                tot ++; dfs(i); tot--; //因为虚边是最后一次加入的,tot会多加一次
            }
        
        for(int i = 1;i <= n; i++) 
            if(!vis[i] && out[i]) {
                tot ++; dfs(i);
            }
        
        printf("%d\n", tot - 1);
        for(int i = 1;i <= tot; i++) {
            printf("%d", (int)a[i].size());
            for(int j = 0;j < (int)a[i].size(); j++) 
                printf(" %d", a[i][j]);
            printf("\n");
            a[i].clear();
        }
    }

    fclose(stdin); fclose(stdout);
    return 0;
}
posted @ 2020-09-25 22:49  C锥  阅读(155)  评论(0编辑  收藏  举报