[日常训练] Graph
来源:互联网 发布:windows live登录 编辑:程序博客网 时间:2024/06/05 08:03
Time limit: 2s Memory limit: 512MB
Description
- 给定一张
n 个点m 条边的无向图,每条边连接两个顶点,保证无重边自环,不保证连通 - 你想在这张图上进行若干次旅游,每次旅游可以任选一个点
x 作为起点,再走到一个与x 直接有边相连的点y ,再走到一个与y 直接有边相连的点z 并结束本次旅游 - 作为一个旅游爱好者,你不希望经过任意一条边超过一次,注意一条边不能即正向走一次又反向走一次,注意点可以经过多次,在满足此条件下,你希望进行尽可能多次的旅游,请计算出最多能进行的旅游次数并输出任意一种方案
Input Format
- 第
1 行两个正整数n 与m ,表示全图的点数与边数下接m 行,每行两个数字u 与v 表示一条边
Output Format
- 第
1 行一个整数cnt 表示答案下接cnt 行,每行三个数字x,y,z ,表示一次旅游的路线如有多种旅行方案,任意输出一种即可
Sample Input
- 4 5 1 2 3 2 2 4 3 4 4 1
Sample Output
- 2 4 1 2 4 3 2
Constraints
- 对于前 20% 的数据,
n≤10,m≤20 - 对于令 20% 的数据,
m=n−1 ,并且图连通 - 对于令 10% 的数据,每个点的度数不超过
2 - 对于 100% 的数据,
n≤100000,m≤200000
Solution 构造 + DFS
- 显然,题目是要我们尽可能多地找出像下面这样的“东西”把整张图覆盖完
- 考虑到这里有一个公共点
y ,我们不妨把这两条边看作是分配到点y 上 - 那么显然,当一个点分配的边数为偶数时,我们可以通过两两任意组合构造出一种匹配完所有边的方案,为奇数时则会剩下一条边
- 进一步的,我们可以通过这一性质构造出一种最优方案
- 首先说一下结论:对于一个边数为
m 的连通快,最优方案必然有⌊m2⌋ 条旅行路线 - 我们先对这张图构造出一棵
DFS 树,显然图中有非树边和树边两种边 - 然后按照
DFS 的顺序处理分配方案,也就是处理到某一节点x 时,它子树中所有点的分配方案已经处理完了 - 那么此时对于这个点
x :它所能选择的边有它的父亲边,它未被选择过的儿子边以及连向它的未被选择过的非树边 - 我们把后两者先分配到点
x ,那么我们就总能通过调整父亲边是否分配给点x 来使得点x 分配到的边数固定为偶数 - 于是最后只可能在根节点处剩下一条边,并且是在总边数为奇数的情况下,则我们上面所说的结论成立,也因此构造出了一组方案
Code
#include <iostream>#include <cstdio>#include <cctype>#include <cstring>#include <algorithm> using namespace std;const int S = 1 << 20;char frd[S], *hed = frd + S;const char *tal = hed;inline char nxtChar(){ if (hed == tal) fread(frd, 1, S, stdin), hed = frd; return *hed++;}inline int get(){ char ch; int res = 0; while (!isdigit(ch = nxtChar())); res = ch ^ 48; while (isdigit(ch = nxtChar())) res = res * 10 + ch - 48; return res;}inline void put(int x){ if (x > 9) put(x / 10); putchar(x % 10 + 48); }const int N = 1e5 + 5, M = N << 2;int n, m, len, Ans;bool vis[N], stp[M];struct Edge { int num, to; Edge *nxt;};Edge p[M], *T = p, *lst[N];Edge q[M], *Q = q, *rst[N];inline void LinkEdge(int x, int y, int z){ (++T)->nxt = lst[x]; lst[x] = T; T->to = y; T->num = z; (++T)->nxt = lst[y]; lst[y] = T; T->to = x; T->num = z;}inline void RinkEdge(int x, int y){ (++Q)->nxt = rst[x]; rst[x] = Q; Q->to = y; ++len;}inline void Dfs(int x, int fa, int I){ int cnt = 0, y; for (Edge *e = lst[x]; e; e = e->nxt) { if (vis[y = e->to]) { if (y == fa || stp[e->num]) continue; ++cnt; stp[e->num] = true; RinkEdge(x, y); continue; } vis[y] = true; Dfs(y, x, e->num); } for (Edge *e = lst[x]; e; e = e->nxt) if (vis[y = e->to] && !stp[e->num]) { if (y == fa) continue; ++cnt; stp[e->num] = true; RinkEdge(x, y); } if ((cnt & 1) && fa) stp[I] = true, RinkEdge(x, fa); }int main(){ freopen("graph.in", "r", stdin); freopen("graph.out", "w", stdout); n = get(); m = get(); int x, y; for (int i = 1; i <= m; ++i) { x = get(); y = get(); LinkEdge(x, y, i); } for (int i = 1; i <= n; ++i) if (!vis[i]) { len = 0; vis[i] = true; Dfs(i, 0, 0); Ans += len >> 1; } put(Ans), putchar('\n'); for (int i = 1; i <= n; ++i) for (Edge *e = rst[i]; e && e->nxt; e = e->nxt->nxt) { put(e->to), putchar(' '); put(i), putchar(' '); put(e->nxt->to), putchar('\n'); } fclose(stdin); fclose(stdout); return 0;}
阅读全文
1 0
- [日常训练] Graph
- HEU日常训练10.02
- 日常训练小结
- 日常训练20161012 道路网
- 日常训练20161012 醉酒
- 日常训练20161014 跟踪
- 日常训练20161018 证据
- 日常训练20161018 subset
- 日常训练 平均数
- 日常训练 水箱
- 日常训练 棋盘游走
- 日常训练 20170531 数字
- 日常训练 20170531 探险
- 日常训练 20170531 矩阵
- 日常训练 20170602 Book
- 日常训练 20170602 Equation
- 日常训练 20170603 棋盘
- 日常训练 20170605 EasyProblem
- jQ4 操作样式
- Spring MVC系列(一):创建MVC项目
- 自定义view之绘画太极图
- 【Scikit-Learn 中文文档】模型评估: 量化预测的质量
- struts2 控制标签
- [日常训练] Graph
- 怎么可以吃菇菇,菇菇辣么可爱 | 钛空舱蘑菇星球
- Ajax技术
- jQ5 事件与动画
- hdoj-1556Color the ball(树状数组)
- 详解大端模式和小端模式
- laravel 使用测试工厂Factory添加测试数据
- java 数组
- 在pom.xml中指定主类