zoukankan      html  css  js  c++  java
  • 贪心法

    搜索算法和动态规划是在多种策略中选取最优解,贪心算法是遵循某种规则,不断地选取当前最优策略。 

    一、硬币问题

    有1元,5元,10元,50元,100元,500元的硬币各C1,C5,C10,C50,C100,C500枚。现在用这些硬币来支付A元,最少需要多少枚硬币。

    策略是尽可能选取面额大的硬币

     1 int main()
     2 {
     3     int ans=0;
     4     int V[6]={1,5,10,50,100,500};
     5     int C[6];
     6     int A;
     7     cin>>A;
     8     for(int i=0;i<6;i++)
     9     {
    10         cin>>C[i];
    11     }
    12     for(int i=5;i>=0;i--)
    13     {
    14         int t=min(A/V[i],C[i]);
    15         A-=t*V[i];
    16         ans+=t;
    17     }
    18     cout<<ans<<endl;
    19     return 0;
    20 }
    View Code

    二、区间问题

    有n项工作。每项工作分别在si时间开始,在ti时间结束。对于每项工作,你都可以选择参与与否。如果选择了参与,那么自始至终都必须参与。此外,参与工作的时间段不能重复。

    选择的策略是:在可选的工作中,每次都选取结束时间最早的工作。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int max_n=100000;
     4 int n,s[max_n],t[max_n];
     5 pair<int,int> itv[max_n];
     6 int ans=0;
     7 int main()
     8 {
     9     cin>>n;
    10     for(int i=0;i<n;i++)
    11     {
    12         cin>>s[i]>>t[i];
    13         //对pair进行的是字典序比较
    14         //为了让结束时间早的工作排在前面
    15         itv[i].first=t[i];
    16         itv[i].second=s[i];
    17     }
    18     sort(itv,itv+n);
    19 
    20     //t是所选工作的结束时间
    21     int t=0;
    22     for(int i=0;i<n;i++)
    23     {
    24         if(t<itv[i].second)
    25         {
    26             ans++;
    27             t=itv[i].first;
    28         }
    29     }
    30     cout<<ans<<endl;
    31     return 0;
    32 }
    View Code

    三、字典序最小问题

    给定长度为n的字符串s,要构造一个长度为n的字符串t,起初,t是一个空串,随后反复进行如下操作。

    1.从s的头部删除一个字符,加到t的尾部

    2.从s的尾部删除一个字符,加到t的尾部

    目标是要构造字典序尽可能小的字符串t。

    选择的策略是:按照字典序比较s和将s反转后的s‘

    如果s较小,就从s的开头取出一个文字,加到t的末尾

    如果s’较小,就从s‘的开头取出一个文字,加到t的末尾。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int max_n=2000;
     4 int n;
     5 char s[max_n+1];
     6 int main()
     7 {
     8     cin>>n;
     9     cin>>s;
    10     int a=0,b=n-1;
    11     while(a<=b)
    12     {
    13         //将从左起和右起的字符串比较
    14         bool left=false;
    15         for(int i=0;i+a<=b;i++)
    16         {
    17             if(s[a+i]<s[b-i])
    18             {
    19                 left=true;
    20                 break;
    21             }
    22             else if(s[a+i]>s[b-i])
    23             {
    24                 left=false;
    25                 break;
    26             }
    27         }
    28         if(left) putchar(s[a++]);
    29         else putchar(s[b--]);
    30     }
    31     putchar('
    ');
    32     return 0;
    33 }
    View Code

    四、

    直线上有n个点,点i的位置是xi。从这n个点中选择若干个,给他们加上标记。对每一个点,其距离为r以内的区域里必须有带有标记的点(自身也算)。至少要有多少点被加上标记。(poj3069)代码是求解大致流程,不完全符合题目。

    策略是:首先选取最左边的点右侧距离最左点距离为R以内的最远点。标记后,从标记点+r的距离开始寻找“最左点”,重复操作。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int max_n=2000;
     4 int x[max_n+1];
     5 int n,r;
     6 int main()
     7 {
     8     cin>>n>>r;
     9     for(int i=0;i<n;i++)
    10     {
    11         cin>>x[i];
    12     }
    13     sort(x,x+n);
    14     int i=0,ans=0;
    15     while(i<n)
    16     {
    17         //s是没有被覆盖的最左边的点的位置
    18         int s=x[i++];
    19         //一直前进到距离s大于r的点
    20         while(i<n&&x[i]<=s+r) i++;
    21         int p=x[i-1];
    22         //一直前进道距离p大于r的店
    23         while(i<n&&x[i]<=p+r) i++;
    24         ans++;
    25     }
    26     cout<<ans<<endl;
    27     return 0;
    28 }
    View Code

    五、

    农夫约翰为了修理栅栏,要将一块很长的木板切割成n块。准备切成的木板的长度为l1,l2,...,ln。未切割前木板的长度恰好为切割后木板长度的总和。每次切断木板时,需要的开销为这块木板的长度。求按要求切完的最小开销。只说明了大概意思。

    分析如下:

    切割的方法可以用二叉树来描述。但是我实在懒。所以就简单表述一下这颗树。木板起始长度为15.

                                 15

                           7             8

                      3        4    5      3

                                           1     2

    我猜应该能勉强看出这是一个二叉树。。。然后我们发现开销的合计为 : 所有叶子节点的权值*叶子节点的深度的总和。

    即3*2+4*2+5*2+1*3+2*3=33。

    此时的最佳切割方法首先应该有如下性质:最短的板与次短的板的节点应当是兄弟节点(对于最优解来说,最短的板应该是深度最大的叶子节点之一,所以与这个叶子节点同一深度的兄弟节点一定存在,由于同样是最深的叶子节点,所以应该对应于次短的板)。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int max_n=20000;
     4 int l[max_n+1];
     5 int n;
     6 int ans=0;
     7 int main()
     8 {
     9     cin>>n;
    10     for(int i=0;i<n;i++)
    11     {
    12         cin>>l[i];
    13     }
    14 
    15     //计算到木板为1块为止
    16     while(n>1)
    17     {
    18         //求出最短的板mii1和次短的板mii2
    19         int mii1=0,mii2=1;
    20         if(l[mii1]>l[mii2]) swap(mii1,mii2);
    21         for(int i=2;i<n;i++)
    22         {
    23             if(l[i]<l[mii1])
    24             {
    25                 mii2=mii1;
    26                 mii1=i;
    27             }
    28             else if(l[i]<l[mii2])
    29             {
    30                 mii2=i;
    31             }
    32         }
    33 
    34         //合并两板
    35         int combine=l[mii1]+l[mii2];
    36         ans+=combine;
    37 
    38         //mii1的位置放合并板,mii2的位置放最后一块板
    39         if(mii1==n-1) swap(mii1,mii2);
    40         l[mii1]=combine;
    41         l[mii2]=l[n-1];
    42         n--;
    43     }
    44     cout<<ans<<endl;
    45     return 0;
    46 }
    View Code

    然后可以联想到霍夫曼编码(当码字长度为整数情况时最优的编码方案)。频度较高的字母对应较短的01序列,频度较低的字母对应较长的01序列。将木板换成字符,长度换成频度可以类比。

  • 相关阅读:
    HDU 1010 Tempter of the Bone(DFS剪枝)
    HDU 1013 Digital Roots(九余数定理)
    HDU 2680 Choose the best route(反向建图最短路)
    HDU 1596 find the safest road(最短路)
    HDU 2072 单词数
    HDU 3790 最短路径问题 (dijkstra)
    HDU 1018 Big Number
    HDU 1042 N!
    NYOJ 117 求逆序数 (树状数组)
    20.QT文本文件读写
  • 原文地址:https://www.cnblogs.com/wangkaipeng/p/6425569.html
Copyright © 2011-2022 走看看