头歌题目
任务描述
本关任务:完成 乘船问题 ;
相关知识
为了完成本关任务,你需要掌握:贪心法
;。
贪心法,又称贪婪算法是一种解决问题的策略。是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是最好或最优的算法。如果策略正确,那么贪心法往往是易于描述、易于实现的。
贪心算法与动态规划的不同在于它对每个子问题的解决方案都做出选择,不能回退。动态规划则会保存以前的运算结果,并根据以前的结果对当前进行选择,有回退功能。
贪心法的优缺点
贪心法可以解决一些最优化问题,如:求图
中的最小生成树
、求哈夫曼编码
……对于其他问题,贪心法一般不能得到我们所要求的答案。一旦一个问题可以通过贪心法来解决,那么贪心法一般是解决这个问题的最好办法。由于贪心法的高效性以及其所求得的答案比较接近最优结果,贪心法也可以用作辅助算法或者直接解决一些要求结果不特别精确的问题。
下面通过一个简单的实例题目对贪心进行详解:
例题:
乘船问题: 给出 n
个人,第 i
个人重量为 wi 。每艘船的最大载重量均为 C
,且最多只能乘两个人。用最少的船装载所有人。有解输出船的个数,无解输出 “no”。
解题分析
考虑最轻的人i
,如果每个人都无法和他一起坐船,则唯一的方法就是每个人一艘船。否则,他应该选择能和他一起坐船的人中最重的一个j
。这样的方法是贪心的,因此它只是让眼前的浪费最少。
程序实现
将人的重量按照从小到大排序。比j
更重的人只能每人坐一艘船。这样,只需要两个下表i
和j
分别表示当前考虑的最轻的人和最重的人,每次先将j
往左移动,直到i
和j
可以共坐一艘船,然后将i+1
,j-1
,并重复上述操作。
关键代码
int p=0,q=n-1,s=0; // p代表左索引,q代表右索引
if(a[q]>c) cout<<"no"<<endl; // 如果最大的体重(最右边索引的人)大于船的最大载重,那么直接输出 no
else{
for(int i=0;i<n;i++){ // 遍历所有人
if(p>q) break;
if(a[p]+a[q]>c) q--; // 如果最小体重的人和当前最大的人不能同船,那么当前体重最大的人就要一个人一个船。
else p++,q--; // 当前体重最小的人和当前体重最大的人同船
s++;
}
cout<<s<<endl;
}
时间复杂度:不难看出,程序的时间复杂度仅为O(n)
。是最优算法。
编程要求
根据提示,在右侧编辑器补充代码,完成乘船问题。
测试说明
平台会对你编写的代码进行测试:
测试输入:
6 85
5 84 85 80 84 83
预期输出: 5
测试输入:
10 85
5 84 85 80 84 83 55 77 6 11
预期输出: 7
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
constexpr auto maxn = 10000; // 相当于#define maxn 10000
int n, m, in;
vector<int> vec; // 存储人的体重
bool vis[maxn]; // 用于标记上船的人
bool cmp(int a, int b) {
return a > b;
}
int main()
{
ios::sync_with_stdio(false);
cin >> n >> m; // 上船人数和船的承重
for (int i = 0; i < n; i++) {
cin >> in;
vec.push_back(in);
}
sort(vec.begin(), vec.end(), cmp);
int start = 0, end = vec.size() - 1, ans = 0;
while (start < end) {
++ans;
int sum = vec[start] + vec[end];
if (sum <= m) {
vis[start] = vis[end] = true;
start++;
end--;
}
else {
vis[start] = true;
start++;
}
}
if (start == end && vis[start] == false) {
ans++;
}
cout << ans;
return 0;
}