搭船的题解

该文是一个关于算法设计的问题,描述了如何计算在一定的村庄和村民需求下,船夫运送所有村民到达目的地的最小行驶距离。关键在于处理逆路村民的行程,通过排序和合并重叠部分来减少总路程。最后,提供了C++代码实现这一算法。
摘要由CSDN通过智能技术生成

搭船

题目描述

有一条河,沿河有 m + 1 m+1 m+1 个村庄,按照次序依次编号为 0 0 0 m m m,相邻村庄之间的距离都是 1 1 1

某天早上,船夫在 0 0 0 号村庄开始他的工作,当天想要搭船的村民有 n n n 个,其中第 i i i 个村民要从 a i a_i ai 号村庄搭船到 b i b_i bi 号村庄。完成运输所有村民的工作后,船夫需要将船停到 m m m 号村庄上。

船夫的船舱足够大,可以同时搭载任意多名村民。显然,同时搭载多名村民,可以减少重复路程。请帮助船夫计算一下,为了把所有村民运到各自的目的地,在当天的工作中,船只最少需要行走多少距离?1

输入格式

第一行:两个整数 n n n m m m

第二行到第 n + 1 n+1 n+1 行:第 i + 1 i+1 i+1 行有两个整数表示 a i a_i ai b i b_i bi

输出格式

单个整数:表示船只最少需要行走多少路程才能把所有村民运到他们的目的地。

样例

输入数据#1

2 10 
2 8 
6 4

输出数据#1

14

输入数据#2

8 15 
1 12 
3 1 
3 9 
4 2 
7 13 
12 11 
14 11 
14 13

输出数据#2

27

数据范围

  • 对于 30 % 的数据满足: 1 ≤ n ≤ 30 , 3 ≤ m ≤ 1 0 3 。 对于 30\%的数据满足:1\leq n\leq 30,3\leq m\leq 10^3。 对于30%的数据满足:1n303m103

  • 对于 50 % 的数据满足: 1 ≤ n ≤ 10000 , 3 ≤ m ≤ 1 0 9 。 对于 50\% 的数据满足:1\leq n\leq 10000,3\leq m\leq 10^9。 对于50%的数据满足:1n100003m109

  • 对于 100 % 的数据满足: 1 ≤ n ≤ 300000 , 3 ≤ m ≤ 1 0 9 。 对于 100\% 的数据满足:1\leq n\leq 300000,3\leq m\leq 10^9。 对于100%的数据满足:1n3000003m109

  • 0 ≤ a i , b i ≤ m 0\leq a_i,b_i \leq m 0ai,bim

    一、审题

    意思是:每个村民要搭船从 a a a b b b,船从 0 0 0 点出发,送完村民后到 m m m 点,问船行驶的最短距离。

    二、看数据范围

    n < = 300000 n<=300000 n<=300000,就知道算法的复杂度是 O ( n ) O(n) O(n) O ( n log ⁡ n ) O(n\log n) O(nlogn); m < = 1 0 9 m<=10^9 m<=109,能看出不能将每个位置都用数组保存起来,并且要用 long long

    三、思路

    1. 首先,顺路的村民(即 a < b a<b a<b)不用考虑,并且不会影响最终的答案。因为船从 0 0 0 出发,最终要停到 m m m,因此顺路的村民直接搭船即可。搭载他们的总路程就是 m m m
    2. 逆路的村民无论如何都是需要逆向开船的,而上面我们知道顺路的村民不需要考虑。因此在输入时,我们 只把逆路的村民记录在结构体里
    3. 对于逆路的村民,我们考虑优化:
      • 如果一个村民的路程被另一个村民的路程完全覆盖,被覆盖的那个村民的路程不用考虑,因为在搭载另一个村民时他“顺路”。
      • 如果两个村民的路程中间有一部分有重合(只有端点重合也可以包括在内),那可以把他俩的路程放到一起去送,这样能节省一些路程。节省的路程就是重合部分的路程的一来一回的距离。
    4. 综合以上分析,为了方便处理。我们可以把逆路的村民的路程按左端点从小到大的排序,如果左端点相同,按右端点从大到小排序。
#include <bits/stdc++.h>
using namespace std;
long long n, m, x, y, ll, sum;
struct ppp {
	long long l, r;
	bool operator<(const ppp& ppr) const {
		if (l != ppr.l) return l < ppr.l;
		return r > ppr.r;
	}
} a[300005];
int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		cin >> x >> y;
		if (x > y) a[++ll] = {y, x};
	}
	sort(a + 1, a + ll + 1);
	for (int i = 1; i <= ll;) {
		long long j, st = a[i].l, en = a[i].r;
		for (j = i + 1; j <= ll; j++) {
			if (a[j].l <= en)
				en = max(en, a[j].r);
			else
				break;
		}
		sum += 2 * (en - st);
		i = j;
	}
	cout << sum + m;
	return 0;
}

  1. 本题来自Topscoding ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

10247D

我会继续努力,信息技术会越好

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值