独木桥(C++,贪心)

(ps:虽说这题是我在贪心类型里面做到的,但我完全没明白到底哪里是贪心)

题目背景

战争已经进入到紧要时间。你是运输小队长,正在率领运输部队向前线运送物资。运输任务像做题一样的无聊。你希望找些刺激,于是命令你的士兵们到前方的一座独木桥上欣赏风景,而你留在桥下欣赏士兵们。士兵们十分愤怒,因为这座独木桥十分狭窄,只能容纳 1 1 1 个人通过。假如有 2 2 2 个人相向而行在桥上相遇,那么他们 2 2 2 个人将无法绕过对方,只能有 1 1 1 个人回头下桥,让另一个人先通过。但是,可以有多个人同时呆在同一个位置。

题目描述

突然,你收到从指挥部发来的信息,敌军的轰炸机正朝着你所在的独木桥飞来!为了安全,你的部队必须撤下独木桥。独木桥的长度为 L L L,士兵们只能呆在坐标为整数的地方。所有士兵的速度都为 1 1 1,但一个士兵某一时刻来到了坐标为 0 0 0 L + 1 L+1 L+1 的位置,他就离开了独木桥。

每个士兵都有一个初始面对的方向,他们会以匀速朝着这个方向行走,中途不会自己改变方向。但是,如果两个士兵面对面相遇,他们无法彼此通过对方,于是就分别转身,继续行走。转身不需要任何的时间。

由于先前的愤怒,你已不能控制你的士兵。甚至,你连每个士兵初始面对的方向都不知道。因此,你想要知道你的部队最少需要多少时间就可能全部撤离独木桥。另外,总部也在安排阻拦敌人的进攻,因此你还需要知道你的部队最多需要多少时间才能全部撤离独木桥。

输入格式

第一行共一个整数 L L L,表示独木桥的长度。桥上的坐标为 1 , 2 , ⋯   , L 1, 2, \cdots, L 1,2,,L

第二行共一个整数 N N N,表示初始时留在桥上的士兵数目。

第三行共有 N N N 个整数,分别表示每个士兵的初始坐标。

输出格式

共一行,输出 2 2 2 个整数,分别表示部队撤离独木桥的最小时间和最大时间。 2 2 2 个整数由一个空格符分开。

样例 #1

样例输入 #1

4
2
1 3

样例输出 #1

2 4

提示

对于 100 % 100\% 100% 的数据,满足初始时,没有两个士兵同在一个坐标, 1 ≤ L ≤ 5 × 1 0 3 1\le L\le5\times 10^3 1L5×103 0 ≤ N ≤ 5 × 1 0 3 0\le N\le5\times10^3 0N5×103,且数据保证 N ≤ L N\le L NL

解题思路:

注意点1:士兵只能位于整数位置,可以有多个士兵位于同一位置,士兵相遇时会改变方向

也就是说,士兵在同一点时不一定改变自己的方向,只有方向不同时才会改变方向

接下来说明几种特殊情况,当士兵间距为1相向而行时,士兵会在1s后回到自己的原位,但是方向相反

当一名士兵遇到多名士兵时,多名士兵会同时反向,由此可见,其实在同一点的若干士兵可以看做一个士兵

注意点2:桥不应该被理解为线段,而应该被理解为点的集合,桥长代表点的数量,0 和 L+1点代表撤离点(区别于端点),相邻两点间距为1

接下来对解题思路进行说明

(1)最短时间:

最短时间 = max{士兵到撤离点距离的绝对值}

求最短时间时,只需要让士兵向着离自己最近的一端前进即可,很容易证明

(2)最长时间:

<1>如果只有一名士兵,最长时间是其到远端撤离点的距离

<2>有多名士兵,为了说明方便,我们把最左端士兵称为left,最右端士兵称为right,设 L 1 = r i g h t − l e f t L_1 = right - left L1=rightleft那么有最长时间 = L 1 L_1 L1 + max{left, L + 1 - right}

接下来进行证明

我们只需要考虑 L 1 L_1 L1范围内的情况,至于为什么emmm,因为来到这个范围以外的士兵行为是单一的,后续处理就是简单的“+ max{left, L + 1 - right}",这点不予证明

如果有两名士兵,显然让他们对撞才是最长时间 L 1 L_1 L1

为了之后便于理解,这里说明一下,会发生对撞的两名士兵,从出发到再次回到起点,所需时间即为二者间距

如果有三名士兵,现在证明最长时间为 L 1 L_1 L1

两侧的士兵一定要向中间走,这是显然的

假设第三名士兵(之后称为middle)在中点右侧,现在讨论他的起始方向

middle若起始向左走,那么一定在中点左侧与left对撞,之后再与right对撞

D 1 = m i d d l e − l e f t D_1 = middle - left D1=middleleft D 2 = r i g h t − m i d d l e D_2 = right - middle D2=rightmiddle

T r i g h t = D 1 = m i d d l e − l e f t < r i g h t − l e f t = L 1 T_{right} = D_1 = middle - left < right - left = L_1 Tright=D1=middleleft<rightleft=L1

T m i d d l e = D 1 2 + D 2 + D 1 2 = D 1 + D 2 = r i g h t − m i d d l e = L 1 T_{middle} = \frac{D_1}{2} + D_2 + \frac{D_1}{2} = D_1 + D_2 = right - middle = L_1 Tmiddle=2D1+D2+2D1=D1+D2=rightmiddle=L1

T l e f t = D 1 2 + D 2 + D 1 2 = D 1 + D 2 = r i g h t − m i d d l e = L 1 T_{left} = \frac{D_1}{2} + D_2 + \frac{D_1}{2} = D_1 + D_2 = right - middle = L_1 Tleft=2D1+D2+2D1=D1+D2=rightmiddle=L1

middle若起始向右走,则在middle与right对撞后,middle会再与left对撞

T r i g h t = D 2 2 + D 1 + D 2 2 = L 1 T_{right} = \frac{D_2}{2} + D_1 + \frac{D_2}{2} = L_1 Tright=2D2+D1+2D2=L1

T m i d d l e = D 2 2 + D 1 + D 2 2 = L 1 T_{middle} = \frac{D_2}{2} + D_1 + \frac{D_2}{2} = L_1 Tmiddle=2D2+D1+2D2=L1

T r i g h t = D 2 < L 1 T_{right} = D_2 < L_1 Tright=D2<L1

综上所述,最长时间为 L 1 L_1 L1

上面的没看懂也没有关系,现在我要说的才是最关键的
两名士兵对撞你可以看作他们穿过了对方
为什么呢?想一下你就明白了,无论是位置还是方向,能不能穿过对方都没有任何区别
是不是豁然开朗了?最长时间还需要证明吗?

实现代码如下:

#include <iostream>
#include <cmath>
using namespace std;

int main() {
	int L, N, soldier, min, max, min_middle;
	double middle;//中点不一定是整数
	cin >> L >> N;
	min = L + 1, middle = double(L + 1) / 2, min_middle = L + 1, max = 0;
	for (int i = 0; i < N; i++) {
		cin >> soldier;
		min > soldier ? min = soldier : min = min;
		max < soldier ? max = soldier : max = max;		
		abs(middle - min_middle) > abs(middle - soldier) ? min_middle = soldier : min_middle = min_middle;
	}
	if (min_middle < L + 1 - min_middle) cout << min_middle << ' ';
	else cout << L + 1 - min_middle << ' ';
	if (min < L + 1 - max) cout << L + 1 - min << endl;
	else cout << max << endl;
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
C++贪心法背包问题可以通过以下步骤解决: 1. 首先,定义一个结构体或类来表示物品,包括物品的重量和价值。 ```cpp struct Item { int weight; int value; }; ``` 2. 创建一个函数来比较两个物品的价值密度(价值除以重量),并按照价值密度从大到小排序物品。 ```cpp bool compare(Item a, Item b) { double ratioA = (double)a.value / a.weight; double ratioB = (double)b.value / b.weight; return ratioA > ratioB; } ``` 3. 实现贪心算法来选择物品并装满背包。首先,将物品按照价值密度从大到小排序。然后,从价值密度最高的物品开始,依次将物品放入背包,直到背包装满或没有物品可选。 ```cpp double knapsackGreedy(vector<Item>& items, int capacity) { sort(items.begin(), items.end(), compare); double totalValue = 0.0; int currentWeight = 0; for (int i = 0; i < items.size(); i++) { if (currentWeight + items[i].weight <= capacity) { currentWeight += items[i].weight; totalValue += items[i].value; } else { int remainingCapacity = capacity - currentWeight; totalValue += (double)remainingCapacity / items[i].weight * items[i].value; break; } } return totalValue; } ``` 4. 创建一个测试函数来验证贪心算法的正确性。 ```cpp void testKnapsackGreedy() { vector<Item> items = {{10, 60}, {20, 100}, {30, 120}}; int capacity = 50; double maxValue = knapsackGreedy(items, capacity); cout << "Max value: " << maxValue << endl; } ``` 5. 调用测试函数来运行贪心算法。 ```cpp int main() { testKnapsackGreedy(); return 0; } ``` 这样就可以使用贪心算法解决C++背包问题了。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

WitheredSakura_

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值