贪心策略笔记
概念:在学贪心之前,我们要先了解贪心是什么东西。首先,贪心是一种策略,一种思想。在对问题进行求解时,总是能作出当前看来最好的选择,但是它不是从整体最优考虑,它所作出的仅是在某种意义上的局部最优解。
贪心没有固定的算法框架,它在不同的题目中出现的形式都是不一样的。但是它算法设计的关键是贪心策略的选择。也就是说想要贪心,你就得从题目的某个条件为基础,根据这个条件选择如何能得到局部的最优解。贪心在大多数情况下都是能将题目AC的,但是如果遇到了某种问题,就能在不是完全AC的情况下得到最高的分数,这就是“贪心”策略名字的由来。
解题思路:
1、看看这题是否适合贪心(如果这道题都不能用贪心的话,那不就等于跳楼吗)
2、选择贪心的标准(这个是关键)
3、根据标准把求解的问题分成若干个子问题
4、对每一个子问题求解,得到子问题的局部最优解
5、把每个子问题的解局部最优解合成原来问题的一个解
贪心就概念而言是十分抽象的,想要真正了解贪心的精髓就得从具体的例题中获得,不然贪心是很难学会的。
智力大冲浪:
题意描述(简化版):
一开始有m元钱,有时间分为n个时段(一个时间段为一个单位时间,又是游戏个数)(n≤500),之间有很多小游戏,每个小游戏都必须在规定期限ti时时间段之前完成(1≤ti≤n)。
如果一个游戏没能在规定期限前完成,则要从奖励费m元中扣去一部分钱wi(每个游戏只占一个单位的时间,且只要在规定时段之前完成就不会扣钱),问
分析:按题意可知,既然想赢得最多的钱,那么扣得钱数肯定要最少,那么显然是肯定要根据钱数进行从大到小排序,先把扣的钱数最多的游戏完成。那么这时就会有一个问题,可能会有重复。那么根据这一个问题,我们可以设置一个布尔数组,用来记录当前这个时段有没有被其他的游戏占掉。如果有,那么时段就推前;如果推前的时段又被占了,就继续推前……依次类推,知道找到没有被占用的位置;但是如果之前的时段全部被占用了,那么这个钱就会被扣除。
现给一组样例解释:
10000//
钱数
7//
有
7
个游戏
4 2 4 3 1 4 6//
每个游戏对应的完成时间段
70 60 50 40 30 20 10//
每个游戏未完成要扣除的钱数
那么首先将这个样例进行从大道小排序:
扣除钱数 |
完成时间段 |
70 |
4 |
60 |
2 |
50 |
4 |
40 |
3 |
30 |
1 |
20 |
4 |
10 |
6 |
好,根据这组样例,我们来模拟:
先完成钱数为70的游戏,然后第4个时间段记为false。
接下来完成钱数60的游戏,然后第二个时间段即为false、
然后完成钱数为50的游戏,那么这是时间段4已经被占了,接下来时间推前一格,第3个时间段记为false;
接着就是40,继续模拟可知3、2都没了,只能占1.
然后是30,但是通过模拟知道1时间段已经被占了,所以钱数只能被扣除。
20也被扣除
10可以完成
那么钱数就是被扣除了50,结果即为9950.
那么具体代码如下:
、#include<bits/stdc++.h>
usingnamespace
std
;
struct qaq{//结构体
long
int
time
;//时间段
long
int
money
;//输掉后丢失的钱数
}
a
[501];
boolsort1(qaq
x
,qaqy
){
return
x
.money
>y
.money
;
}//排序条件
intmain(){
long
int
s
,n
,m
,i
,j
,k
;
bool
f
[501]={};
cin
>>s
>>n
;
for
(
i
=1;i
<=n
;i
++)cin
>>a
[i
].time
;
for
(
i
=1;i
<=n
;i
++)cin
>>a
[i
].money
;
sort(
a
+1,a
+n
+1,sort1
);
for
(
i
=1;i
<=n
;i
++){
k
=a
[i
].time
;//记录当前时间段
while
(
f
[k
]&&k
>0)k
--;//找到没被占用的时间段
if
(
k
>0)f
[k
]=1;//如果找到了,那么久占用掉
else
s
-=a
[i
].money
;//不然就扣钱
}
cout
<<s
;
return
0;
}
修理牛棚:
题意描述:
农民想要修理他的牛棚。他的新木材供应者将会供应他任何他想要的长度,但是供应者只能提供有限数目的木板。农民约翰想将他购买的木板总长度减到最少。给出M(1<= M<=50),可能买到的木板最大的数目;S(1<=S<=200),牛棚的总数;C(1 <= C <=S)牛棚里牛的数目,和牛所在的牛棚的编号stall_number(1<= stall_number <= S),计算拦住所有有牛的牛棚所需木板的最小总长度。输出所需木板的最小总长度作为的答案。
分析:由题意可知,输出的是要最小值,我们得先把牛棚的编号进行排序。先假设一块木板将整个牛棚覆盖,但是题目木板块又是有限的,所以我们一共要把牛棚分成M-1段,显然是再看牛棚之间的间隙,间隙小的先不用管,找出之间间隙前M-1大的进行分区,算出分区长度,再用尾长度到头长度的距离减去分区的间隔即为所求。(每两个木块的间隔为1)
同样再来一组样例:
4 50 18//
最多只能用
4
块木板,那么就是能被分成
3
段。
50
为牛棚总数,
18
为有牛的牛棚总数
3
4
6
8
14
15
16
17
21
25
26
27
30
31
40
41
42
那么根据之间的间距大小可分为一下三段:
3
4
6
8
//第一段
14
15
16
17
21
//第二段
25
26
27
30
31
//第三段
40
41
42
43
那么根据之间的间隙可得知 (43-3+1)-5-3-8=25
25即为所求
代码如下:
#include<bits/stdc++.h>
usingnamespace
std
;
longint
i
,j
,n
,m
,c
,a
[201],sum
=0,x
,b
[201];
boolf
[201];
intmain(){
cin
>>m
>>n
>>c
;
for
(
i
=1;i
<=c
;i
++)cin
>>a
[i
];
sort(
a
+1,a
+c
+1);
for
(
i
=1;i
<=c
-1;i
++)b
[i
]=(a
[i
+1]-a
[i
])-1;//求间隔
for
(
i
=1;i
<=m
-1;i
++)
for
(
j
=i
+1;j
<=c
-1;j
++)
if
(
b
[i
]<b
[j
]){
x
=b
[i
];b
[i
]=b
[j
];b
[j
]=x
;//如上,用m-1块木板讲绳子分成m段,那么就找前m-1大的间隔长
}
sum
=a
[c
]-a
[1]+1;//记录头与尾的距离
for
(
i
=1;i
<=m
-1;i
++)sum
-=b
[i
];//减去间隔
cout
<<sum
;
return
0;
}