usaco Riding the Fences

 给出F个边,求欧拉路径,题目保证一定存在。

单纯DFS的方法求解会超时。

剪枝是看某位大神题解明白的。http://blog.csdn.net/fairyroad/article/details/7563449这是大神链接。

后来才知道这种剪枝就是Fleury算法。

我的代码不同之处是剪枝的实现用的是并查集,这样效率高一点,而且并查集虽然操作多,但每个操作都很简单,编程复杂度低些。

/*
ID: modengd1
PROG: fence
LANG: C++
*/
#include <iostream>
#include <stdio.h>
#include <queue>
#include <memory.h>
#include <vector>
#include <algorithm>
using namespace std;
#define MaxE 1024
#define MaxV 500
int N;
struct node
{
    int Edge;
    int E;
    friend bool operator<(node n1,node n2)
    {
        return n1.E<n2.E;
    }
    node(int edge,int ee)
    {
        Edge=edge;E=ee;
    }
    node(){}
};
pair<int,int> edges[MaxE];
vector< vector<node> > V;
bool visE[MaxE+1];//每条边的访问情况
int Ocounter[MaxV+1];//每个节点的度
int ans[MaxE+1];
int parent[MaxV+1];
void Init()
{
    for(int i=1;i<=MaxV;i++)
    {
        parent[i]=i;
    }
}
int findP(int x)
{
    if(parent[x]==x)
        return x;
    else
    {
        return parent[x]=findP(parent[x]);
    }
}
void Union(int x,int y)
{
    int xp=findP(x),yp=findP(y);
    parent[yp]=xp;
}
bool islegal()//检查是否存在将为遍历过的所有边变成了不连通
{
    int haveEdge[MaxV+1];
    memset(haveEdge,false,sizeof(haveEdge));
    Init();
    for(int i=0;i<N;i++)
    {
        if(!visE[i])
        {
            Union(edges[i].first,edges[i].second);
            haveEdge[edges[i].first]=true;
            haveEdge[edges[i].second]=true;
        }
    }
    int p=MaxE;
    for(int i=1;i<=MaxV;i++)
    {
        if(haveEdge[i])
        {
            p=findP(i);
            break;
        }
    }
    for(int i=1;i<=MaxV;i++)
    {
        if(haveEdge[i]&&findP(i)!=p)
            return false;
    }
    return true;
}
bool DFS(int start,int deep,int sum)//起始节点,已经遍历的边的个数,总编的个数
{
    if(deep==sum)
    {
        for(int i=0;i<=sum;i++)
        {
            printf("%d\n",ans[i]);
        }
        return true;
    }
    else
    {
        if(!islegal())
            return false;
        for(int i=0;i<V[start].size();i++)
        {
            if(!visE[V[start][i].Edge])
            {
                visE[V[start][i].Edge]=true;
                ans[deep+1]=V[start][i].E;
                if(DFS(V[start][i].E,deep+1,sum))
                    return true;
                visE[V[start][i].Edge]=false;
            }
        }
        return false;
    }
}

int main()
{
    freopen("fence.in","r",stdin);
    freopen("fence.out","w",stdout);

    int a,b;
    int start1;//编号最小的出度为奇数的节点
    int start2;//编号最小的出度大于0的点
    memset(visE,false,sizeof(visE));

    //输入
    V.clear();
    V.resize(MaxV+1);
    scanf("%d",&N);
    for(int i=0;i<N;i++)
    {
        scanf("%d%d",&a,&b);
        V[a].push_back(node(i,b));//用i给每条边编号
        V[b].push_back(node(i,a));
        Ocounter[a]++;
        Ocounter[b]++;
        edges[i].first=a;
        edges[i].second=b;
    }

    //所有点的邻接边按照连接的点的序号从小到大排列,以保证字典序
    for(int i=1;i<=MaxV;i++)
    {
        if(V[i].size()>1)
        {
            sort(V[i].begin(),V[i].end());
        }
    }

    //寻找起始点
    start1=start2=0;
    for(int i=1;i<=MaxV;i++)
    {
        if((Ocounter[i]&1)&&start1==0)//找出编号最小的出度为奇数的节点
        {
            start1=i;
        }
        else if(Ocounter[i]>0&&start2==0)//找出编号最小的出度大于0的点
        {
            start2=i;
        }
        if(start1!=0&&start2!=0)
            break;
    }

    if(start1!=0)//如果找到了度为奇数的点
    {
        ans[0]=start1;
        DFS(start1,0,N);
    }
    else
    {
        ans[0]=start2;
        DFS(start2,0,N);
    }
    return 0;
}

  

posted on 2015-09-21 21:02  insaneman  阅读(152)  评论(0)    收藏  举报

导航