1.4.2 修理牛棚
题目考查
贪心算法, 思维考察
解题思路
本题相较上题而言, 思维难度很明显提升了. 这里给出两种供参考的解法
解法一: 扩增法
首先如果木板的数量大于牛的总数, 答案直接就是牛的总数了.
否则, 我们一定会有一些木板覆盖空牛棚, 我们想让木板总长度最小, 就相当于覆盖尽可能少的空牛棚.
假设牛棚编号从左到右依次为1~S, 我们把所有牛所在位置按编号从小到大排序, 记录在**a[]**中.
很显然, 覆盖[1, a[1])区间的空牛棚没有意义, (a[c], S]区间同理.
我们可以这样做: 记录a[i+1]到a[i]之间的空牛棚数量gapi = a[i+1] - a[i] - 1. 假设我们给每个有牛的牛棚都盖上一块长度为1板, 则会有c - 1个gap出现. 但是我们只有m块板, 只允许有m - 1个gap出现. 因此我们需要额外覆盖(c - 1) - (m - 1)个gap, 因此我们贪心的选取c - m个较小的gap即可.
解法二: 删减法
PS: 本解法的变量命名同解法一.
我们不妨假设[a[1], a[c]]区间用一块木板覆盖了, 但实际上我们可以用m块, 我们可以想象成把一块木板分成了m块, 相当于我们可以删除m - 1个gap. 因此我们贪心的删除最大的m - 1个gap即可.
需要注意的是, 我们一共只有c - 1个gap, 因此要么直接判断掉 m >= c的情况, 否则应注意次数不应超过c - 1次.
题目细节
- 当涉及到区间统计时, 我们要定义好变量的含义, 并注意是否需要±1.
AC代码
/* 解法一: 扩增法 */
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 210;
int a[N];
int main()
{
int m, s, c; cin >> m >> s >> c;
rep(i, c) scanf("%d", &a[i]);
sort(a + 1, a + 1 + c);
vector<int> gap;
rep(i, c - 1) gap.push_back(a[i + 1] - a[i] - 1);
sort(gap.begin(), gap.end());
int res = c;
rep(i, c - m) res += gap[i - 1];
cout << res << endl;
return 0;
}
/* 解法二: 删减法 */
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 210;
int a[N];
int main()
{
int m, s, c; cin >> m >> s >> c;
rep(i, c) scanf("%d", &a[i]);
sort(a + 1, a + 1 + c);
priority_queue<int> q;
rep(i, c - 1) q.push(a[i + 1] - a[i] - 1);
int res = a[c] - a[1] + 1;
rep(i, m - 1) {
res -= q.top(), q.pop();
if (q.empty()) break;
}
cout << res << endl;
return 0;
}