贪心策略笔记

贪心策略笔记

概念:在学贪心之前,我们要先了解贪心是什么东西。首先,贪心是一种策略,一种思想。在对问题进行求解时,总是能作出当前看来最好的选择,但是它不是从整体最优考虑,它所作出的仅是在某种意义上的局部最优解。
         贪心没有固定的算法框架,它在不同的题目中出现的形式都是不一样的。但是它算法设计的关键是贪心策略的选择。也就是说想要贪心,你就得从题目的某个条件为基础,根据这个条件选择如何能得到局部的最优解。贪心在大多数情况下都是能将题目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>
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),计算拦住所有有牛的牛棚所需木板的最小总长度。输出所需木板的最小总长度作为的答案。

分析:由题意可知,输出的是要最小值,我们得先把牛棚的编号进行排序。先假设一块木板将整个牛棚覆盖,但是题目木板块又是有限的,所以我们一共要把牛棚分成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>
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;//如上,用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;
}

猜你喜欢

转载自blog.csdn.net/huang_ke_hai/article/details/78648445