题意是给一个无重边无自环的连通无向图,每次取出两条有公共点的边,求一种取边方案。无解时输出no solution。
很容易想,无解等价于边数为奇数。有解的时候靠构造。构造是一个猜想+调整的过程。
可以肯定的是,要对边遍历一遍。但是如果先匹配父亲的边再匹配儿子的边的话,可能会造成把原图分裂成两个子图并且有个子图边数为奇数进而导致无解的情况。
所以想到先匹配儿子的边,这样,父亲的边可以适时调整。需要注意的是,虽然是按边遍历,但是不能由儿子访问父亲,因为这样会输出重边。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <stack>
#define clr(A) memset(A,0,sizeof(A))
#define mm 100005
using namespace std;
typedef long long LL;
typedef pair<int, int > P;
vector<P> by[mm];
int ans[mm][3],c = 0;
vector<int> E[mm];
int vis[mm],used[mm];
int n,m;
int dfs(int pre,int cur)
{
used[cur] = 1;
for(int i = 0; i < by[cur].size();i++)
{
int u = by[cur][i].first, d = by[cur][i].second;
if(vis[d]||used[u]) continue; //边不能重复访问,不能访问祖先
vis[d] = 1;
// printf("edge = %d %d\n",cur,u);
if(dfs(cur,u)) E[cur].push_back(u);
}
used[cur] = 0;
int i;
for(i = 0; i+1<E[cur].size();i+=2)
{
ans[c][0] = E[cur][i];
ans[c][1] = cur;
ans[c][2] = E[cur][i+1];
c++;
}
if(i<E[cur].size()) {
ans[c][0] = pre;ans[c][1] = cur;ans[c++][2] = E[cur][i];
E[cur].clear();return 0;
}
else {E[cur].clear();return 1;} //E[cur]要清空一下,因为cur可能会再次被遍历,但E[cur]中的边已经用过。
}
int main()
{
freopen("CF.in","r",stdin);
scanf("%d%d",&n,&m);
if(m & 1) {printf("No solution\n");return 0;}
for(int i =1; i<=n;i++)
if(!by[i].empty()) by[i].clear();
for(int i =1; i<=n;i++)
if(!E[i].empty()) E[i].clear();
for(int i = m;i>=1;i--)
{
int a,b;
scanf("%d%d",&a,&b);
by[a].push_back(P(b,i));
by[b].push_back(P(a,i));
}
clr(vis);
dfs(-1,1);
for(int i = 0;i<c;i++) printf("%d %d %d\n",ans[i][0],ans[i][1],ans[i][2]);
return 0;
}