[USACO1.3] 修理牛棚 Barn Repair
题目描述
在一个月黑风高的暴风雨夜,Farmer John 的牛棚的屋顶、门被吹飞了 好在许多牛正在度假,所以牛棚没有住满。
牛棚一个紧挨着另一个被排成一行,牛就住在里面过夜。有些牛棚里有牛,有些没有。 所有的牛棚有相同的宽度。
自门遗失以后,Farmer John 必须尽快在牛棚之前竖立起新的木板。他的新木材供应商将会供应他任何他想要的长度,但是吝啬的供应商只能提供有限数目的木板。 Farmer John 想将他购买的木板总长度减到最少。
给出 m , s , c m,s,c m,s,c,表示木板最大的数目、牛棚的总数、牛的总数;以及每头牛所在牛棚的编号,请算出拦住所有有牛的牛棚所需木板的最小总长度。
输入格式
一行三个整数
m
,
s
,
c
m,s,c
m,s,c,意义如题目描述。
接下来
c
c
c 行,每行包含一个整数,表示牛所占的牛棚的编号。
输出格式
输出一行一个整数,表示所需木板的最小总长度。
样例 #1
样例输入 #1
4 50 18
3
4
6
8
14
15
16
17
21
25
26
27
30
31
40
41
42
43
样例输出 #1
25
提示
【数据范围】
对于
100
%
100\%
100% 的数据,
1
≤
m
≤
50
1\le m \le 50
1≤m≤50,
1
≤
c
≤
s
≤
200
1\le c \le s \le 200
1≤c≤s≤200。
USACO Training Section 1.3
题目简化:
一条长度为s的线段上有c个点,采用n条长度不限的线段将这些点全部覆盖,问线段长度和的最小值是多少?
思考:
这道题目中,哪些是变量,哪些是不变量?
我们的目标是什么?
显然,对于这道题而言,不变的量是线段中点的数量,而变量就是这n条线段的放置方式,不同的放置方式得到的线段长度是不同的。
而我们的目标就是保证在点全部被线段覆盖到的前提下,线段的长度和最小。
题目中的不变量就使得我们有很多方法去达到我们想要的目标,但是最优解只有一种。
如何决策?
我们先从简单的出发:
-
如果只有一条线段,你会选择如何覆盖?
这样简单吧,直接把线段中最左端存在的点和最右端存在的点用一条线段连起来就好了。 -
两条呢?
问题似乎复杂了一点。但是我们从目标出发:保证点全部被覆盖的前提下使得线段长度和尽可能小。
对于点全部被覆盖这个条件,前一条我们已经保证了,于是我们可以在前一条的前提下进行逆向操作:删除线段。
如何删除线段可以不影响点的覆盖情况呢?
简单,删除两个点之间的空隙线段。
这样一来,既能保证点全部被覆盖,又能减少线段的长度和,问题是不是就迎刃而解了呢?
在此基础上,我们采用一点贪心算法,使得每次删除的空隙长度的线段尽可能长,我们就能使线段的长度和尽可能小。
完结撒花
#include<bits/stdc++.h>
using namespace std;
const int N = 10010;
int a[N],b[N];
int n,m,c;
int main(){
scanf("%d %d %d",&m,&n,&c);
for (int i = 1; i <= c; i++) scanf("%d",&a[i]);
sort(a+1,a+c+1);
int sum = a[c]-a[1]+1;
for (int i = 1; i < c; i++) b[i] = a[i+1]-a[i]-1;
sort(b+1,b+c);
for (int i = c-1; i >= c-m+1; i--) sum-=b[i];
cout<<sum;
}
总结:
做人能贪心就贪心
正难则反