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;
}
浙公网安备 33010602011771号