8.30 巫师之旅

题意

给出一张\(n\)个点\(m\)条边的无向图,定义一次旅行为任选一个点\(x\)作为起点,再走到一个与\(x\)直接连接的点\(y\),再走到一个与\(y\)直接连接的点\(z\)结束本次旅行

要求每条边(无向边)只能被经过一次,而点没有限制

请计算出可能进行的最多的旅游次数并输出其中任意一种方案


解法

这题考试时想出来了,但打得代码又臭又长。。

可以玩一玩,发现对于一个连通块,它的答案就是其中的边数除以\(2\)

本题的难点在于方案的输出

考虑对于一颗树,我们怎样输出方案

可以规定合并的顺序为先儿子,后父亲

即如果该点有儿子,则把儿子两两进行合并;如果该点有奇数个儿子,则把最后一个儿子与父亲合并并打上标记

这样一定能构造出一个合法的方案,因为每一个结点一定是由它的父亲进行更新的

考虑更复杂的情况,即原题所要求的图

我们可以发现图其实就是树的情况拉上了很多条非树边

非树边可以与儿子放在一起,规则同上

这样就能构造出一个合法解了


代码

就不放考场上打得\(3kb\)奇丑无比的代码了。。

#include <cstdio>
#include <queue>
#include <cstring>

using namespace std;

const int N = 4e5 + 10;

int n, m;

int cap = 1;
int head[N], to[N << 1], nxt[N << 1];

int cnt, num;
int bfn[N], fa[N], can[N], ans[N][3];

int book[N], vis[N];

queue<int> que;

inline void add(int x, int y) {
    to[++cap] = y, nxt[cap] = head[x], head[x] = cap;
}

void init(int rt) {
    cnt = 0;
    que.push(rt), book[rt] = 1;
    while (!que.empty()) {
        int x = que.front(); que.pop();
        bfn[++cnt] = x;
        for (int i = head[x]; i; i = nxt[i]) {
            if (book[to[i]])    continue;
            que.push(to[i]);
            fa[to[i]] = x, book[to[i]] = 1;
        }
    }   
}

void solve(int rt) {
    init(rt);
    for (int i = cnt; i >= 1; --i) {
        int x = bfn[i], tot = 0;
        for (int j = head[x]; j; j = nxt[j]) 
            if (!vis[j] && to[j] != fa[x])  can[++tot] = j;
        for (int j = head[x]; j; j = nxt[j])
            if (!vis[j] && to[j] == fa[x])  can[++tot] = j;
        for (int j = 1; j + 1 <= tot; ++j) {
            vis[can[j]] = vis[can[j] ^ 1] = 1;
            ans[++num][0] = to[can[j]];
            ++j;
            ans[num][1] = x;
            vis[can[j]] = vis[can[j] ^ 1] = 1;
            ans[num][2] = to[can[j]];
        }
    }
}

int main() {
    
    scanf("%d%d", &n, &m);
    
    int u, v;
    for (int i = 1; i <= m; ++i) {
        scanf("%d%d", &u, &v);
        add(u, v), add(v, u);
    }
    
    for (int i = 1; i <= n; ++i) 
        if (!book[i])   solve(i);   
    
    printf("%d\n", num);
    for (int i = 1; i <= num; ++i)  printf("%d %d %d\n", ans[i][0], ans[i][1], ans[i][2]);
    
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/VeniVidiVici/p/11436338.html