送货(csp201512-4) :
问题描述
题目简述
为了增加公司收入,F公司新开设了物流业务。由于F公司在业界的良好口碑,物流业务一开通即受到了消费者的欢迎,物流业务马上遍及了城市的每条街道。然而,F公司现在只安排了小明一个人负责所有街道的服务。
任务虽然繁重,但是小明有足够的信心,他拿到了城市的地图,准备研究最好的方案。城市中有n个交叉路口,m条街道连接在这些交叉路口之间,每条街道的首尾都正好连接着一个交叉路口。除开街道的首尾端点,街道不会在其他位置与其他街道相交。每个交叉路口都至少连接着一条街道,有的交叉路口可能只连接着一条或两条街道。
小明希望设计一个方案,从编号为1的交叉路口出发,每次必须沿街道去往街道另一端的路口,再从新的路口出发去往下一个路口,直到所有的街道都经过了正好一次。
输入/输出格式
输入格式:
输入的第一行包含两个整数n, m,表示交叉路口的数量和街道的数量,交叉路口从1到n标号。
接下来m行,每行两个整数a, b,表示和标号为a的交叉路口和标号为b的交叉路口之间有一条街道,街道是双向的,小明可以从任意一端走向另一端。两个路口之间最多有一条街道。
输出格式:
如果小明可以经过每条街道正好一次,则输出一行包含m+1个整数p1, p2, p3, …, pm+1,表示小明经过的路口的顺序,相邻两个整数之间用一个空格分隔。如果有多种方案满足条件,则输出字典序最小的一种方案,即首先保证p1最小,p1最小的前提下再保证p2最小,依此类推。
如果不存在方案使得小明经过每条街道正好一次,则输出一个整数-1。
样例
输入样例:
4 5
1 2
1 3
1 4
2 4
3 4
输出样例:
1 2 4 1 3 4
问题分析
解题思路
这个题是一个无向图求欧拉道路的一道练习题。欧拉道路的判断在无向图中只需要判断每个点的度数就可以了,只要在连通的无向图中每个点的度数均为偶数或者有两个点的度数为奇数且一个为起点,这个图就存在欧拉道路。然后就可以用类似于dfs的方法构造欧拉道路了。注意这个题是需要输出字典序最小的欧拉道路,因此,需要事先对边进行排序,并且需要使用栈来存储。
参考代码
#include <iostream>
#include <cstring>
#include <stack>
#include <vector>
#include <algorithm>
using namespace std;
int m,n;
int dgrs[10010];
bool vis[10010][10010];
stack<int> s;
vector<int> edges[10010];
void init()
{
memset(dgrs,0,sizeof(dgrs));
memset(vis,false,sizeof(vis));
}
bool have_eular()
{
int cnt=0;
for(int i=1;i<=n;i++)
{
if(dgrs[i]%2!=0) cnt++;
}
if(cnt==0||(cnt==2&&dgrs[1]%2!=0)) return true;
else return false;
}
void create_eular(int start)
{
vis[start][0]=true;
for(int i=0;i<edges[start].size();i++)
{
if(vis[start][edges[start][i]]==0)
{
vis[start][edges[start][i]]=true;
vis[edges[start][i]][start]=true;
create_eular(edges[start][i]);
s.push(edges[start][i]);
}
}
}
int main()
{
init();
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d %d",&u,&v);
edges[u].push_back(v);
edges[v].push_back(u);
dgrs[u]++;
dgrs[v]++;
}
if(!have_eular()) printf("-1");
else
{
for(int i=1;i<=n;i++)
{
sort(edges[i].begin(),edges[i].end());
}
create_eular(1);
for(int i=1;i<=n;i++)
{
if(!vis[i][0])
{
printf("-1");
return 0;
}
}
s.push(1);
while(!s.empty())
{
printf("%d ",s.top());
s.pop();
}
}
return 0;
}
心得体会
这个题虽然思路比较简单,但是坑还是很多的。首先是连通图这个条件很容易忽略,导致最后会扣10分。之后是内存溢出的问题,10000*10000的矩阵存图内存直接爆炸。所以不要因为这样存可以免于排序而浪费空间。另外,我的一个陋习:vis数组用int存也导致了内存溢出。以后还是要注意这一点。