1738 蹄球(思维题、基环树)

1. 问题描述:

为了准备即将到来的蹄球锦标赛,Farmer John 正在训练他的 N 头奶牛(方便起见,编号为 1…N)进行传球。这些奶牛在牛棚一侧沿直线排列,第 i 号奶牛位于距离牛棚 xi 的地方。每头奶牛都在不同的位置上。在训练开始的时候,Farmer John 会将若干个球传给不同的奶牛。当第 i 号奶牛接到球时,无论是从 Farmer John 或是从另一头奶牛传来的,她会将球传给最近的奶牛(如果有多头奶牛与她距离相同,她会将球传给这些奶牛中最左边的那头奶牛。)。为了使所有奶牛都有机会练习到传球,Farmer John 想要确保每头奶牛都持球至少一次。帮助他求出为了达到这一目的他开始时至少要传出的球的数量。假设他在开始的时候能将球传给最适当的一组奶牛。

输入格式

输入的第一行包含 N。第二行包含 N 个用空格分隔的整数,其中第 i 个整数为 xi。

输出格式

输出 Farmer John 开始的时候最少需要传出的球的数量,使得所有奶牛至少持球一次。

数据范围

1 ≤ N ≤ 100,
1 ≤ xi ≤ 1000

输入样例:

5
7 1 3 11 4

输出样例:

2
样例解释
在上面的样例中,Farmer John 应该将球传给位于 x=1 的奶牛和位于 x=11 的奶牛。位于 x=1 的奶牛会将她的球传给位于 x=3 的奶牛,在此之后这个球会在位于 x=3 的奶牛和位于 x=4 的奶牛之间来回传递。位于 x=11 的奶牛会将她的球传给位于 x=7 的奶牛,然后球会被传给位于 x=4 的奶牛,在此之后这个球也会在位于 x=3 的奶牛和位于 x=4 的奶牛之间来回传递。这样的话,所有的奶牛都会至少一次接到球(可能从 Farmer John,也可能从另一头奶牛)。可以看出,不存在这样一头奶牛,Farmer John 可以将球传给她之后所有奶牛最终都能被传到球。
来源:https://www.acwing.com/problem/content/description/1740/

2. 思路分析:

分析题目可以知道实际上我们已知的是一个数轴,数轴上有若干个点,直接做是不好做的,我们需要分析一下题目具有哪些性质,我们可以将每一头牛看成是图论中的一个点(这一点是核心),每一个点有唯一一条出边,至多有两条入边,由这两个特点我们可以知道实际上是一个基环图(环上挂着许多树的图),如下图所示,实际上要求解的是最少选择多少个点作为起点使得所有点至少被访问一次:

并且结合这道题目,可以发现是一个特殊的基环图:环的长度一定是2,所以题目中的基环图有两种类型如下图所示:

根据上面的图片我们可以知道对于第一种环上挂了一些树的图可以统计入度为0的点的个数,对于第二种图选择其中一个点作为起点即可,我们可以将第一种入度为0的点的权重看成是1,第二种图中点的权重看成是1 / 2,实际在写代码统计的时候可以将每一个点的权重先乘以2,最后结果除以2就可以避免就精度的问题。

3. 代码如下:

class Solution:
    def process(self):
        # 将其看成是图论的问题
        n = int(input())
        q = list(map(int, input().split()))
        # 按照坐标从小到大排序
        q.sort()
        INF = 10 ** 8
        # 加上左右边界的两个哨兵这样就不会走出边界(常用的一个技巧)
        q.insert(0, -INF)
        q.append(INF)
        # p[i]表示节点i走向了哪一个点
        p, d = [0] * (n + 10), [0] * (n + 10)
        for i in range(1, n + 1):
            # 向左走
            if q[i] - q[i - 1] <= q[i + 1] - q[i]:
                # 表示i走向了i - 1
                p[i] = i - 1
                d[i - 1] += 1
            # 向右走
            else:
                # 表示i走向了i + 1
                p[i] = i + 1
                d[i + 1] += 1
        # 枚举
        res = 0
        # 入度为0的权重为2, 只有两个点的环的权重为1, 这样可以避免精度的问题最终除以2即可
        for i in range(1, n + 1):
            if d[i] == 0:
                res += 2
            elif p[p[i]] == i and d[i] == 1 and d[p[i]] == 1:
                res += 1
        # 因为每一个点的权重多乘以一个2, 所以除以2就是最终的答案
        return res // 2


if __name__ == "__main__":
    print(Solution().process())
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值