HDU 6311 Cover(欧拉回路)

题目链接

Cover

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1723    Accepted Submission(s): 377
Special Judge

 

Problem Description

The Wall has down and the King in the north has to send his soldiers to sentinel.
The North can be regard as a undirected graph (not necessary to be connected), one soldier can cover one path. Today there's no so many people still breathing in the north, so the King wants to minimize the number of soldiers he sent to cover each edge exactly once. As a master of his, you should tell him how to arrange soldiers.

 

Input

There might be multiple test cases, no more than 20. You need to read till the end of input.
In the first line, two integers n and m, representing the number of nodes and edges in the graph.
In the following m lines, each contain two integers, representing two ends of an edge.
There are no parallel edges or self loops.
1≤n,m≤100000

 

Output

For each test case, the first line contains number of needed routes, p.
For the following p lines, an integer x in the beginning, followed by x integers, representing the list of used edges. Every integer should be a positive or negative integer. Its absolute value represents the number of chosen edge (1~n). If it's positive, it shows that this edge should be passed as the direction as the input, otherwise this edge should be passed in the direction different from the input. Edges should be in correct order.

Sample Input

 

3 3

1 2

1 3

2 3

Sample Output

 

1

3 1 3 -2

 题意:

给你一个任意的图,让你最少画几笔,使得所有的边被画过至少一次

解析:

首先我网上搜了一下欧拉回路的算法,发现有3个

1.Fleury算法:       代码

2.基本(套圈)法    

3.Hierholzer算法(邻接表)    Hierholzer算法(矩阵)

然后上官方题解

每个连通块显然是独立的。对于一个连通块(除了单个点的),如果奇度数点个数为 kk,那么至少需要 max(k/2,1)条路径。我们将奇度数点两两配对连边,求出欧拉回路,然后把这些边删掉,就可以变成恰好 max(k/2,1)条路径。

复杂度 O(n+m)。

根据dls大神的讲解,我自己大概梳理了一下

在一个无向图中,将两个度数为奇数的点连起来,就可以消去两个奇数点。
所以如果一个无向图要有欧拉回路(全部点的度数全为偶数),只要加k/2条边就可以了。
所以只要将一个连通块变成欧拉图,搜一遍得到路径,再将我们人为添加的边删掉,
剩下的就是这个连通块的"笔画"了。

这里为什么一定变成欧拉回路而不是欧拉通路,其实这里欧拉回路和欧拉通路都是可以的,得到的答案是一样的。
主要是算法实现上的问题,如果你变成欧拉通路,你就一定得从剩下的两个奇数点出发,终止,
并且好像网上的代码大多都是找欧拉回路的。
然后为什么这里加k/2条边后扫出的结果删掉多加的边就是答案,并且这个答案=max(k/2,1)
因为一个图中,我们从奇数点开始走,停止的也一定是在奇数点,那么再一遍遍不重复地从图中走出一条条类似前面地路径
(奇数点开始,奇数点结束)然后我们将这些路径地终点连接下一条路径的起点(多加的边),这样又因为每一条路径的起止点都为奇数点,一条边删去两个奇数点。所以最终把他们连成欧拉回路所需的边的数量就是k/2。

然后我看了大佬的代码,发现其实大佬把很多网上说的特殊情况都已经通过自己的算法给解决了...

所以可以直接当作模板用.....

主要思想是

1.建图、找连通块

2.找各个连通块内的奇数点

3.①若连通块奇数点数为0 ,直接找欧拉回路,得到一条路径

②否则,加k/2条边,找欧拉回路,在找到的路径中删去人为加的边,得到k/2条路径

4.删去记录路径答案的数组中空的情况

​​​​#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define pb push_back
using namespace std;
const int MAXN = 1e5+10;
typedef struct elem
{
    int u;
    int v;
    int next;
}elem;
int n,m,cnt;
elem edge[MAXN*5];
int head[MAXN],w;
int fa[MAXN],deg[MAXN];
vector<int> ji[MAXN];
vector<int> ans[MAXN*5];
int stk[MAXN*5],top;
int vis[MAXN*5];


int findfa(int x)
{
    return x==fa[x]?x:(fa[x]=findfa(fa[x]));
}

inline void addedge(int u,int to)
{
    edge[cnt].u=u;
    edge[cnt].v=to;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}

void dfs(int s)   //基本(套圈)法
{
    for(int k=head[s];k!=-1;k=edge[k].next)
    {
        if(!vis[k>>1])
        {
            vis[k>>1]=1;
            dfs(edge[k].v);
            stk[top++]=(k&1?-(k>>1):(k>>1));
            /*if(k&1) stk[top++]=-(k>>1);
            else stk[top++]=(k>>1);*/
        }
    }
}


void solve()
{
    memset(vis,0,sizeof(vis));
        memset(head,-1,sizeof(head));
        for(int i=1;i<=n;i++) ji[i].clear(),fa[i]=i,deg[i]=0;
        cnt=2;
        w=0;
        for(int i=1;i<=m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            addedge(x,y);
            addedge(y,x);
            deg[x]++;
            deg[y]++;
            fa[findfa(y)]=findfa(x);

        }
        for(int i=1;i<=n;i++)
        {
            if(deg[i]&1)                     //这里要注意因为连接两个集合时可能只是将两个集合的头节点连接了。
            {
                ji[findfa(i)].pb(i);        //!!!!!!这里因为i所对应的头节点可能已经指向其他头节点
            }
        }

        for(int i=1;i<=n;i++)
        {
            if(fa[i]!=i) continue;
            if(ji[i].size()==0)
            {
                top=0;
                dfs(i);
                w++;
                while(top) ans[w].pb(stk[top-1]),top--;
            }
            else
            {
                for(int j=0;j<ji[i].size();j+=2)
                {
                    addedge(ji[i][j],ji[i][j+1]);
                    addedge(ji[i][j+1],ji[i][j]);
                }
                top=0;
                dfs(ji[i][0]);   //从奇数点开始找欧拉回路
                w++;
                while(top)
                {
                    if(stk[top-1]>m||stk[top-1]<-m)
                    {
                        w++;
                        top--;
                        continue;
                    }
                    ans[w].pb(stk[top-1]);
                    top--;
                }
                /*dfs(i);   //用于从任意点开始找欧拉回路
                vector<int> pos;
                for(int i=top-1;i>=0;i--)
                    if(stk[i]>m||stk[i]<-m)
                        pos.push_back(i);
                for(int i=0;i<pos.size()-1;i++)
                {
                    w++;
                    for(int j=pos[i]-1;j>pos[i+1];j--)
                        ans[w].push_back(stk[j]);
                }
                w++;
                for(int j=pos[pos.size()-1]-1;j>=0;j--)
                    ans[w].push_back(stk[j]);
                for(int j=top-1;j>pos[0];j--)
                    ans[w].push_back(stk[j]);*/

            }

        }
        int num=0;
        for(int i=1;i<=w;i++)
        {
            num+=(ans[i].size()==0?0:1);
            /*if(ans[i].size()!=0)
            {
                num++;
            }*/
        }
        printf("%d\n",num);
        for(int i=1;i<=w;i++)
        {
            if(ans[i].size()!=0)
            {
                printf("%d",ans[i].size());
                for(int j=0;j<ans[i].size();j++)
                {
                    printf(" %d",ans[i][j]);
                }
                printf("\n");
                ans[i].clear();
            }
        }
}


int main()
{
    cnt=0;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        solve(); //时间复杂度为O(n+m)
    }
    return 0;
}

​​​​

猜你喜欢

转载自blog.csdn.net/qq_37025443/article/details/81261139