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序列。将木板换成字符,长度换成频度可以类比。

  • 相关阅读:
    API入门系列之三 那迷惑人的Windows字符和字符指针类型 转载
    laravel中关联模型查询选择性的字段
    【实习】微软PM实习生面经
    【C++学习】String类的基本用法
    sql server cast 和 convert函数使用
    JS,Jquery获取,dropdownlist,checkbox 下拉列表框的值
    Buffer
    SQL Server 2012新增的内置函数尝试
    SQL Server2012新特性WITH RESULT SETS
    ros(8)自定义service数据
  • 原文地址:https://www.cnblogs.com/wangkaipeng/p/6425569.html
Copyright © 2011-2022 走看看