demon日常的刷题——贪心(c++版本)

来源:互联网 发布:java short类型 编辑:程序博客网 时间:2024/05/22 11:43

Demon昨天参加了学校的专题练习——贪心。

贪心算法

一、基本概念:
 
     所谓贪心算法是指,在对问题求解时,总是做出在当前看来是最好的选择也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解
贪心算法没有固定的算法框架,算法设计的关键是贪心策略的选择。
必须注意的是,贪心算法不是对所有问题都能得到整体最优解,选择的贪心策略必须具备无后效性,即某个状态以后的过程不会影响以前的状态,只与当前状态有关。
    所以对所采用的贪心策略一定要仔细分析其是否满足无后效性。

二、贪心算法的基本思路:
    1.建立数学模型来描述问题。
    2.把求解的问题分成若干个子问题。
    3.对每一子问题求解,得到子问题的局部最优解。
    4.把子问题的解局部最优解合成原来解问题的一个解。

三、贪心算法适用的问题
      贪心策略适用的前提是:局部最优策略能导致产生全局最优解。
    实际上,贪心算法适用的情况很少。一般,对一个问题分析是否适用于贪心算法,可以先选择该问题下的几个实际数据进行分析,就可做出判断。
 
四、贪心算法的实现框架
    从问题的某一初始解出发;
    while (能朝给定总目标前进一步)
    { 
          利用可行的决策,求出可行解的一个解元素;
    }
    由所有解元素组合成问题的一个可行解;
  
五、贪心策略的选择
     因为用贪心算法只能通过解局部最优解的策略来达到全局最优解,因此,一定要注意判断问题是否适合采用贪心算法策略,找到的解是否一定是问题的最优解。

六、题目详解
1.看电视

题目描述

暑假到了,小明终于可以开心的看电视了。但是小明喜欢的节目太多了,他希望尽量多的看到完整的节目。
现在他把他喜欢的电视节目的转播时间表给你,你能帮他合理安排吗?

输入

输入包含多组测试数据。每组输入的第一行是一个整数n(n<=100),表示小明喜欢的节目的总数。
接下来n行,每行输入两个整数si和ei(1<=i<=n),表示第i个节目的开始和结束时间,为了简化问题,每个时间都用一个正整数表示。
当n=0时,输入结束。

输出

对于每组输入,输出能完整看到的电视节目的个数。

样例输入

121 33 40 73 815 1915 2010 158 186 125 104 142 90

样例输出

5
题解:为了计算节目,我们可以按照结束时间排序:尽可能去找早结束的,然后从第一个开始,记录tv[i].b,然后循环找tv[j].a比tv[i].b大的时候,找的到就num++.最后输出num.
#include<iostream>  #include<algorithm>  using namespace std;  struct cnt{      int a;      int b;  }tv[103];  //结构体排序 int cmd(cnt x,cnt y){      return x.b<y.b;  }  int main(){      int n,i,j,k;      while(cin>>n&&n){   for(i=0;i<n;i++){          cin>>tv[i].a>>tv[i].b;    }      sort(tv,tv+n,cmd);      j=0;      k=0;      for(i=1;i<n;i++){          if(tv[i].a>=tv[j].b){             k++;              j=i;          }  }      cout<<k+1<<endl;      }      return 0;  } 


2.出租车费

题目描述

某市出租车计价规则如下:起步4公里10元,即使你的行程没超过4公里;接下来的4公里,每公里2元;之后每公里2.4元。行程的最后一段即使不到1公里,也当作1公里计费。
一个乘客可以根据行程公里数合理安排坐车方式来使自己的打车费最小。
例如,整个行程为16公里,乘客应该将行程分成长度相同的两部分,每部分花费18元,总共花费36元。如果坐出租车一次走完全程要花费37.2元。
现在给你整个行程的公里数,请你计算坐出租车的最小花费。

输入

输入包含多组测试数据。每组输入一个正整数n(n<10000000),表示整个行程的公里数。
当n=0时,输入结束。

输出

对于每组输入,输出最小花费。如果需要的话,保留一位小数。

样例输入

39160

样例输出

1020.436

题解:关键是找零界点,第一个零界点:我们从题目中可以看得出8是一个循环,每逢8公里时,路费最小就是18;
第二个零界点:我们10+(x-4)*2=2.4*(x),得出x=5,当x<=5时,选择10+(x-4)*2,当x>5选择2.4*x;
#include <iostream>  #include <iomanip>  using namespace std;  int main(){      int n,x;      double s;      while(cin>>n&&n){         if(n<=4) cout<<10<<endl;        else{              s=n/8*18;              x=n%8;              if(x==0) cout<<(int)s<<endl;              else if(x>=5){                  s += 10+(x-4)*2;                  cout<<(int)s<<endl;              }              else{                  s+=2.4*x;                  if(s-(int)s==0) cout<<(int)s<<endl;                  else cout<<fixed<<setprecision(1)<<s<<endl;              }          }      }      return 0;  } 


3.木棒

题目描述

现有n根木棒,已知它们的长度和重量。要用一部木工机一根一根地加工这些木棒。该机器在加工过程中需要一定的准备时间,是用于清洗机器,调整工具和模板的。木工机需要的准备时间如下:
(1)第一根木棒需要1min的准备时间;
(2)在加工了一根长为l,重为w的木棒之后,接着加工一根长为ll(l<=ll),重为ww(w<=ww)的木棒是不需要任何准备时间的。否则需要一分钟的准备时间。
给定n根木棒,你要找到最少的准备时间。例如现在有长和重分别为(4,9),(5,2),(2,1),(3,5)和(1,4)的五根木棒,那么所需准备时间最少为2min,顺序为(1,4),(3,5),(4,9),(2,1),(5,2)。

输入

输入包含多组测试数据。输入的第一行是一个整数T,表示测试数据的个数。
每个测试例两行:
第一行是一个整数n(1<=n<=5000),表示有多少根木棒;
第二行包括n*2个整数,表示了l1,w1,l2,w2,l3,w3,...,ln,wn,这些数均不大于10000,其中li和wi表示第i根木棒的长度和重量。

输出

输出以分钟为单位的最少准备时间。

样例输入

3 5 4 9 5 2 2 1 3 5 1 4 3 2 2 1 1 2 2 3 1 3 2 2 3 1

样例输出

213

题解:贪心,结构体二级排序,目的时尽可能满足题目中的条件,当加工了l,w时,加工ll(l<=ll),ww(w<=ww)不需要准备时间。
先按照木棍的l排序,l相同时,按照w排序;从第一个开始,sum++,同时将所有比a[0].w大和等于的所有的a[i].w置为0.之后遇到a[i].w不为0时,sum++;就是最终解。
#include <algorithm>  #include <iostream>  using namespace std;    struct stick{      int l;      int w;   }a[5000];  int cmp(stick a,stick b){      if(a.l==b.l) return a.w<b.w;        else return a.l<b.l;  }  int main(){        int t,n,sum=0;      cin>>t;      while(t--){             sum=0;          scanf("%d",&n);          for(int i=0;i<n;i++) cin>>a[i].l>>a[i].w;            sort(a,a+n,cmp);int flag;          for(int i=0;i<n;i++){              if(a[i].w){                  sum++;                  flag=a[i].w;                  for(int j=i+1;j<n;j++){                      if(a[j].w>=flag){                          flag=a[j].w;                          a[j].w=0;                      }                  }              }          }          cout<<sum<<endl;      }      return 0;  }


4.迷瘴

题目描述


小明正在玩游戏,他控制的角色正面临着幽谷的考验——
幽谷周围瘴气弥漫,静的可怕,隐约可见地上堆满了骷髅。由于此处长年不见天日,导致空气中布满了毒素,一旦吸入体内,便会全身溃烂而死。
幸好小明早有防备,提前备好了解药材料(各种浓度的万能药水)。现在只需按照配置成不同比例的浓度。
现已知小明随身携带有n种浓度的万能药水,体积V都相同,浓度则分别为Pi%。并且知道,针对当时幽谷的瘴气情况,只需选择部分或者全部的万能药水,然后配置出浓度不大于 W%的药水即可解毒。
现在的问题是:如何配置此药,能得到最大体积的当前可用的解药呢?
特别说明:由于幽谷内设备的限制,只允许把一种已有的药全部混入另一种之中(即:不能出现对一种药只取它的一部分这样的操作)。

输入

输入数据的第一行是一个整数C,表示测试数据的组数;
每组测试数据包含2行,首先一行给出三个正整数n,V,W(1<=n,V,W<=100);
接着一行是n个整数,表示n种药水的浓度Pi%(1<=Pi<=100)。

输出

对于每组测试数据,请输出一个整数和一个浮点数;
其中整数表示解药的最大体积,浮点数表示解药的浓度(四舍五入保留2位小数);
如果不能配出满足要求的的解药,则请输出0 0.00。

样例输入

21 35 681 2 79 2559 63 

样例输出

35 0.010 0.00

题解:题目要求的是体积最大,意味着,我们可以排序浓度数组P,从浓度最小的开始配置,就可以使得,体积最大。
#include <iomanip>    #include <iostream>  #include <cmath>  #include <algorithm>  using namespace std;  int p[105];  int main(){      int t;      cin>>t;      while(t--){          int n,v,w;          int volume=0;          double med=0.0,P=0.00;           cin>>n>>v>>w;         double ww = w / 100.00;          for(int i=0;i<n;i++) cin>>p[i];          sort(p,p+n);          for(int i=0;i<n;i++){              volume+=v;              med = med + p[i] / 100.0 * v;             double x = med / volume;              if(x<ww||fabs(x-ww)<1e-8){                P = x;              } else{                  volume = volume - v;                  break;              }                     }          if(volume) cout<<volume<<" "<<setprecision(2)<<P<<endl;        else cout<<"0 0.00\n";     }      return 0;  }  


0 0
原创粉丝点击