luoguP2731 骑马修栅栏 题解

题目

题目背景

Farmer John每年有很多栅栏要修理。他总是骑着马穿过每一个栅栏并修复它破损的地方。

题目描述

John是一个与其他农民一样懒的人。他讨厌骑马,因此从来不两次经过一个栅栏。你必须编一个程序,读入栅栏网络的描述,并计算出一条修栅栏的路径,使每个栅栏都恰好被经过一次。John能从任何一个顶点(即两个栅栏的交点)开始骑马,在任意一个顶点结束。

每一个栅栏连接两个顶点,顶点用1到500标号(虽然有的农场并没有500个顶点)。一个顶点上可连接任意多(>=1)个栅栏。两顶点间可能有多个栅栏。所有栅栏都是连通的(也就是你可以从任意一个栅栏到达另外的所有栅栏)。

你的程序必须输出骑马的路径(用路上依次经过的顶点号码表示)。我们如果把输出的路径看成是一个500进制的数,那么当存在多组解的情况下,输出500进制表示法中最小的一个 (也就是输出第一位较小的,如果还有多组解,输出第二位较小的,等等)。

输入数据保证至少有一个解。

输入输出格式

输入格式:
第1行: 一个整数F(1 <= F <= 1024),表示栅栏的数目

第2到F+1行: 每行两个整数i, j(1 <= i,j <= 500)表示这条栅栏连接i与j号顶点。

输出格式:
输出应当有F+1行,每行一个整数,依次表示路径经过的顶点号。注意数据可能有多组解,但是只有上面题目要求的那一组解是认为正确的。

输入输出样例

输入样例#1:
9
1 2
2 3
3 4
4 2
4 5
2 5
5 6
5 7
4 6
输出样例#1:
1
2
3
4
2
5
4
6
5
7
说明

题目翻译来自NOCOW。

USACO Training Section 3.3

题解

我们读题可以发现,这道题目是图的遍历问题,而且遍历的是无向图的边(就是要经过每一条栅栏),用深度优先遍历就可以了。
这道题目坑点很多。
详见代码注释

代码

#include<bits/stdc++.h>
int a[2000][2000]={};
//a数组用来存储i和j之间的边数。
int n,du[2000]={},ans[2000]={},top;//du数组用来统计度数(欧拉路的遍历问题一定要统计度数,因为存在奇点的时候一定是从一个奇点出发,回到另外一个奇点)不会欧拉路的可以看看一本通的例题。。ans数组是用来记录路径的。top用来记录ans数组内的元素个数,便于输出。
void read()
{
    memset(a,false,sizeof(a));
    scanf("%d",&n);
    int x,y;
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&x,&y);
        a[x][y]++;//x和y之间的边数+1;
        a[y][x]++;//无向图,要对称
        du[x]++;//x的度增加
        du[y]++;//y的度增加
    }
    return;
}
//read函数是用来初始化和读入数据的
int dfs(int k)
{       
    //从1搜到最大数据。。暴力枚举哈哈哈
    for(int i=1;i<=1050;i++)
    //如果度数不为0,而且k与i之间有没有走过的边,代表可以这样遍历
    if(du[i]>0&&a[k][i])
    {
        a[k][i]--;//标记为已经走过
        a[i][k]--;
        du[k]--;//度数相应自减
        du[i]--;
        dfs(i);//继续深搜
    }
    ans[++top]=k;//记录路径
}
//dfs函数:用来深度优先遍历所有的边
void print()
{
    for(int i=top;i>=1;i--)
    printf("%d\n",ans[i]);
}
//打印输出解,我们在dfs函数中可以看到,我是最后才记录路径的,因此路径在ans数组重视倒序存储的。
int main()
{
    read();
    int k=1;
    for(int i=1;i<=500;i++)
    if(du[i]!=0)
    {
        k=i;
        break;
    } 
    //如果存在欧拉回路,就从第一个有度数的点开始(1、符合题目处理多组解的要求;2、任意一个点开始深搜)
    for(int i=1;i<=500;i++)
    if(du[i]%2==1)
    {
        k=i;
        break;
    }
    //判断是否存在奇点,如果有就要从该点开始深搜。
    dfs(k);
    //开始深搜
    print();
    //输出解
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值