题意:给一个无向图,求用最小的一笔画次数,覆盖整个图,输出边的编号,如果一笔画的过程,走某条边的顺序与题意给的顺序相反,输出负的边编号。
思路:多校第二场的第三题,一笔画走的就是欧拉路,如果题目给的无向图不满足欧拉路性质,那么就要自己加边,使得多余的奇度点变成偶度点,然后再用fleury算法求路径,如果遇到了补充的边,那么就结束这次的记录,开始下次一笔画的记录,思路及代码都是学这位大佬的 DevilInChina 的。
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<stack>
#include<string>
#include<iostream>
using namespace std;
const int maxn=1e5+10;
struct node
{
int v,id,flag;
};
vector<node>edges;
vector<int>G[maxn];
int d[maxn],vis[maxn];
int n,m,cnt,tot;
vector<int>V,ans[maxn];
void init()
{
for(int i=1;i<maxn;i++)
{
G[i].clear();
ans[i].clear();
d[i]=vis[i]=0;
}
edges.clear();
V.clear();
tot=0;
}
void add(int u,int v,int id)
{
edges.push_back((node){v,id,1});
edges.push_back((node){u,id,-1});
int m=edges.size();
G[u].push_back(m-2);
G[v].push_back(m-1);
d[u]++,d[v]++;
}
void dfs(int u)
{
vis[u]=true;
if(d[u]&1)V.push_back(u);
for(int i=0;i<G[u].size();i++)
{
node e=edges[G[u][i]];
int v=e.v;
if(!vis[v])
dfs(v);
}
}
void dfs1(int u)
{
for(int i=0;i<G[u].size();i++)
{
node t=edges[G[u][i]];
int tmp=G[u][i];
int v=t.v;
G[u].erase(find(G[u].begin(),G[u].end(),tmp));
tmp^=1;
G[v].erase(find(G[v].begin(),G[v].end(),tmp));
dfs1(v);
if(t.id<=m)
ans[tot].push_back(t.id*(-t.flag));//求得的路径是倒过来的
else
tot++;//遇到了自己补充的边
}
}
int main()
{
int u,v;
while(~scanf("%d%d",&n,&m))
{
init();
cnt=m;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
add(u,v,i);
}
for(int i=1;i<=n;i++)
if(!vis[i]&&d[i])
{
dfs(i);
if(V.empty()) //如果没有奇度点,将i点设为起点
V.push_back(i);
for(int j=2;j<V.size();j+=2)
{
u=V[j],v=V[j+1];
add(u,v,++cnt);
}
tot++;
dfs1(V[0]);
V.clear();
}
printf("%d\n",tot);
for(int i=1;i<=tot;i++)
{
printf("%d",ans[i].size());
for(int j=0;j<ans[i].size();j++)
printf(" %d",ans[i][j]);
puts("");
}
}
}