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