• 贪心算法


    1.洛谷  P1650 赛马

    2.codevs  2181 田忌赛马

    题目描述

    我国历史上有个著名的故事: 那是在2300年以前。齐国的大将军田忌喜欢赛马。他经常和齐王赛马。他和齐王都有三匹马:常规马,上级马,超级马。一共赛三局,每局的胜者可以从负者这里取得200银币。每匹马只能用一次。齐王的马好,同等级的马,齐王的总是比田忌的要好一点。于是每次和齐王赛马,田忌总会输600银币。

    田忌很沮丧,直到他遇到了著名的军师――孙膑。田忌采用了孙膑的计策之后,三场比赛下来,轻松而优雅地赢了齐王200银币。这实在是个很简单的计策。由于齐王总是先出最好的马,再出次好的,所以田忌用常规马对齐王的超级马,用自己的超级马对齐王的上级马,用自己的上级马对齐王的常规马,以两胜一负的战绩赢得200银币。实在很简单。

    如果不止三匹马怎么办?这个问题很显然可以转化成一个二分图最佳匹配的问题。把田忌的马放左边,把齐王的马放右边。田忌的马A和齐王的B之间,如果田忌的马胜,则连一条权为200的边;如果平局,则连一条权为0的边;如果输,则连一条权为-200的边……如果你不会求最佳匹配,用最小费用最大流也可以啊。 然而,赛马问题是一种特殊的二分图最佳匹配的问题,上面的算法过于先进了,简直是杀鸡用牛刀。现在,就请你设计一个简单的算法解决这个问题。

    输入输出格式

    输入格式:

    第一行一个整数n,表示他们各有几匹马(两人拥有的马的数目相同)。第二行n个整数,每个整数都代表田忌的某匹马的速度值(0 <= 速度值<= 100)。第三行n个整数,描述齐王的马的速度值。两马相遇,根据速度值的大小就可以知道哪匹马会胜出。如果速度值相同,则和局,谁也不拿钱。

    【数据规模】

    对于20%的数据,1<=N<=65;

    对于40%的数据,1<=N<=250;

    对于100%的数据,1<=N<=2000。

    输出格式:

    仅一行,一个整数,表示田忌最大能得到多少银币。

    输入输出样例

    输入样例#1:
    3
    92 83 71
    95 87 74
    输出样例#1:
    200
    思路:贪心!!    
          分别将两个人的马按速度排序,比较过程分为三种,如果从前往后田忌的马的速度值是比齐王的快的,那么答案就+200,指针分别向后移,如果从后往前田忌马的速度是比齐王快的,答案也+200,指针向前移,如果田忌最快的马也比齐王最快的马慢,那么就让田忌最慢的马与齐王最快的马比,答案-200,指向田忌的马的指针后移,指向齐王的马的指针前移。
     
    ╭(╯3╰)╮ 代码:
     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 using namespace std;
     5 
     6 int a[2333],b[2333],n,ans;
     7 
     8 int main() {
     9     cin>>n;
    10     for(int i=1; i<=n; i++) cin>>a[i];
    11     for(int i=1; i<=n; i++) cin>>b[i];
    12     sort(a+1,a+n+1);
    13     sort(b+1,b+n+1);
    14     int tj_b=1,tj_e=n,qw_b=1,qw_e=n;
    15     while(tj_b<=tj_e) {
    16         if(a[tj_b]>b[qw_b]) {
    17             ans+=200,tj_b++,qw_b++;
    18         }
    19         else
    20         if(a[tj_e]>b[qw_e]) {
    21             ans+=200,tj_e--,qw_e--;
    22         }
    23         else{
    24              if(a[tj_b]<b[qw_e]) ans-=200;
    25              tj_b++,qw_e--;
    26         }
    27     }
    28     if(ans>0) cout<<ans;
    29     else cout<<"0";
    30     return 0;
    31 }

    3.洛谷  P1090 合并果子

    4.Codevs 1063 合并果子

    题目描述

    在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。

    每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。

    因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。

    例如有3种果子,数目依次为1,2,9。可以先将1、2堆合并,新堆数目为3,耗费体力为3。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为12,耗费体力为12。所以多多总共耗费体力=3+12=15。可以证明15为最小的体力耗费值。

    输入输出格式

    输入格式:

    输入文件fruit.in包括两行,第一行是一个整数n(1<=n<=10000),表示果子的种类数。第二行包含n个整数,用空格分隔,第i个整数ai(1<=ai<=20000)是第i种果子的数目。

    输出格式:

    输出文件fruit.out包括一行,这一行只包含一个整数,也就是最小的体力耗费值。输入数据保证这个值小于2^31。

    输入输出样例

    输入样例#1:
    3 
    1 2 9 
    
    输出样例#1:
    15
    

    说明

    对于30%的数据,保证有n<=1000:

    对于50%的数据,保证有n<=5000;

    对于全部的数据,保证有n<=10000。

    思路:

      用建一个小根堆,每次都取上面最小的两个加起来,插入堆中,每次取最小值,最后的值就为最小值。

    ╭(╯3╰)╮ 代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<queue>
     4 #include<algorithm>
     5 using namespace std;
     6 
     7 long long n,a,ans;
     8 priority_queue<int>d; 
     9 
    10 int main() {
    11     cin>>n;
    12     for(int i=1; i<=n; i++) {
    13         cin>>a;
    14         d.push(-a);
    15     }
    16     int firs,secon;
    17     for(int i=1; i<n;i++) {
    18         firs=d.top();
    19         d.pop();
    20         secon=d.top();
    21         d.pop();
    22         d.push(firs+secon);
    23         ans+=firs+secon;
    24     }
    25     cout<<-ans<<endl; 
    26     return 0;
    27 }

    5.洛谷  P1809 过河问题_NOI导刊2011提高(01)

    题目描述

    有一个大晴天,Oliver与同学们一共N人出游,他们走到一条河的东岸边,想要过河到西岸。而东岸边有一条小船。 

    船太小了,一次只能乘坐两人。每个人都有一个渡河时间T,船划到对岸的时间等于船上渡河时间较长的人所用时间。 

    现在已知N个人的渡河时间T,Oliver想要你告诉他,他们最少要花费多少时间,才能使所有人都过河。 

    注意,只有船在东岸(西岸)的人才能坐上船划到对岸。

    输入输出格式

    输入格式:

    输入文件第一行为人数N,以下有N行,每行一个数。 

    第i+1行的数为第i个人的渡河时间。

    输出格式:

    输出文件仅包含一个数,表示所有人都渡过河的最少渡河时间

    输入输出样例

    输入样例#1:
    4 
    6 
    7 
    10 
    15 
    输出样例#1:
    42

    说明

    [数据范围] 

    对于40%的数据满足N≤8。 

    对于100%的数据满足N≤100000。

    [样例解释] 

    初始:东岸{1,2,3,4},西岸{} 

    第一次:东岸{3,4},西岸{1,2} 时间7 第二次:东岸{1,3,4},西岸{2} 时间6 第三次:东岸{1},西岸{2,3,4},时间15 第四次:东岸{1,2},西岸{3,4} 时间7 第五次:东岸{},西岸{1,2,3,4} 时间7 

    所以总时间为7+6+15+7+7=42,没有比这个更优的方案。

    思路:

          方法有两种,一种是让最快的跑腿,第一快和第二快的一起过去,让最快的来回接送其他人,这样貌似是最优的,但还有一种情况,就是第一快和第二快过去了,让第一快回来,但不让它回去,让第一慢和第二慢回去,再让第二快回来接第一快,在某些情况下这种情况是比上一种快的,所以我们在两者之间取小,还有就是在只有1、2、3个人时,自己分别判断一下。

    ╭(╯3╰)╮ 代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 using namespace std;
     4 
     5 int n,ans,sped[100001];
     6 int start;
     7 
     8 int main() {
     9     cin>>n;
    10     for(int i=1;i<=n;i++) {
    11         cin>>sped[i];
    12     }
    13     start = n;
    14     while(start) {
    15         if(start==1) { ans+=sped[1];break; }
    16         else if(start==2) { ans+=sped[2]; break; }
    17         else if(start==3) { ans+=sped[1]+sped[2]+sped[3]; break; }
    18         else {
    19             ans+=min(sped[2]+sped[1]+sped[start]+sped[2],sped[start]+2*sped[1]+sped[start-1]);
    20             start-=2;
    21         }
    22     }
    23     cout<<ans<<endl;
    24     return 0;
    25 }

     6.洛谷 P1325 雷达安装

    7.Codevs 2625 雷达安装

    题目描述

    描述:

    假设海岸线是一条无限延伸的直线。它的一侧是陆地,另一侧是海洋。每一座小岛是在海面上的一个点。雷达必须安装在陆地上(包括海岸线),并且每个雷达都有相同的扫描范围d。你的任务是建立尽量少的雷达站,使所有小岛都在扫描范围之内。

    数据使用笛卡尔坐标系,定义海岸线为x轴。在x轴上方为海洋,下方为陆地。

    样例1如图所示

    输入输出格式

    输入格式:

    第一行包括2个整数n和d,n是岛屿数目,d是雷达扫描范围。

    接下来n行为岛屿坐标。

    输出格式:

    一个整数表示最少需要的雷达数目,若不可能覆盖所有岛屿,输出“-1”。

    输入输出样例

    输入样例#1:
    3 2
    1 2
    -3 1
    2 1
    
    输出样例#1:
    2

    思路:

      看到这个题目,一脸懵,怎么办??不会啊~~ 再想想,来我们将题目转化一下就容易多了。

      * 雷达覆盖问题 - 问题转化:
      - 将问题稍微进行转化:将基站设为覆盖半径为 D。
      - 则问题变为:每个基站的覆盖区域必须要有雷达。

      * 雷达覆盖问题 - 问题转化:

      - 又因为雷达只能放在 X 轴上,所以每个基站覆盖的其实是一条线段。

      如何转化成在x轴上的线段呢?? 简单~~ 数学知识(a^2+b^2=c^2)求a的长度(a嘛,就是覆盖区域(圆)与x轴的交点到基站的直线距离;c嘛,就是所说的d喽,覆盖半径;b嘛,就是基点坐标的纵坐标;三者构成三角形)  

      左端点就为原点坐标的x-a,右端点为原点坐标的x+a(用结构体将两者联系一下就是一条线段~(≧▽≦)/~啦)。

      - 则问题变为:每条线段上必须要要有雷达。

      怎么样,现在是不是感觉好理解多了?? 那么,我们的贪心策略是什么(⊙o⊙)?

      先把线段按右端点排个序,对于最早的线段将雷达放在它的右端点上,Why? Because 按右端点排序后,如果第一条线段的右端点比第二条线段的左端点打,说明两者有重叠部分,那么在右端点放置一个雷达就是最优的,(其他方法不会比这种方法更少),如果此线段上已经有了雷达,那么标记一下,从下一个没有雷达的开始重复上述做法(也就是循环了)。

    贪心算法 - 总结

    * 贪心算法是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。——以上来自百度百科。

    * 贪心算法解题的一般步骤:
    1 局部最优,当规模较小的时候做出最优的决策;
    - 举例子,观察数据,大胆猜想
    2 全局最优解,每一步所作的贪心选择最终能得到问题的最优解;
    - 力所能及的时候,小心求证

    自己选的路,跪着也要走完!!

  • 相关阅读:
    4、CreateJS介绍-PreLoadJS
    3、CreateJS介绍-SoundJS
    洛谷 题解 UVA1151 【买还是建 Buy or Build】
    算法详解之拓扑排序
    算法详解之缩点
    洛谷 题解 P3627 【[APIO2009]抢掠计划】
    板娘脚本
    2019.6.20义乌测试赛自我成绩分析
    洛谷 题解 P1225 【黑白棋游戏】
    洛谷 题解 P1908 【逆序对】
  • 原文地址:https://www.cnblogs.com/wsdestdq/p/6822513.html
Copyright © 2020-2023  润新知