2.2一往直前贪心法
贪心
经典问题:取硬币
有1,5,10,50,100,500元硬币各c0,c1,c2,c3,c4,c5枚。现用这些硬币来支付A元,最少需要多少枚?
首先能想到用暴力搜索,枚举完所有的情况即可求出其中最大者。
但对于这类题目,我们通常会想到尽可能地使用大面额的硬币,就有了:在每个阶段,不断选取当前最优策略——贪心算法。
#include<bits/stdc++.h>
//#include<iostream>
using namespace std;
int main ()
{
int a[6]={1,5,10,50,100,500};//硬币的面额
int c[6],A ,ans=0;
for(int i=0;i<6;i++)//读入每种面额硬币的个数
cin>>c[i];
cin>>A;
for(int i=5;i>=0;i--)//尽可能选用大面额硬币
{
int t=min(A/a[i],c[i]);
A-=t*a[i];
ans+=t;
}
cout<<ans<<endl;
return 0;
}
区间问题
n项工作,知道开始时间和结束时间,问最多能不重叠地参与多少项工作?
第一种想法,每次选取开始时间最早的。但有弊端可能持续时间太长反而造成项数减少。(×)
第二种想法,每次选取用时最短的。但可能开始时间太晚反而浪费。(×)
第三种想法,每次选取重叠次数最少。也可举出反例。(×)
第四种想法,每次选取结束时间最早的。结束时间越早,之后可选的工作也就越多。归纳法和反证法能给出这个方法正确处理此问题的一个直观解释。(√)
以下是代码实现:
//区间问题
#include<bits/stdc++.h>
#include<iostream>
using namespace std;
const int maxn=1e5+5;
int s[maxn],t[maxn];
pair<int ,int > a[maxn];//也可以用结构体的comp排序,但这个可以直接用sort更加方便
int main (){
int n,x;
cin>>n;
for(int i=0;i<n;i++){cin>>x;a[i].second=x;}
for(int i=0;i<n;i++){cin>>x;a[i].first=x;}//因为要选取结束时间最早的,即按第二个数组从小到大排列,所以将第二组存到first第一组存到second
sort(a,a+n);//排序
int ans=0,t=0;
for(int i=0;i<n;i++)
{
if(t<a[i].second)//选取最靠近当前结束时间的开始时间(贪心思想)
{
ans++;t=a[i].first;//计数+标记当前结束时间
}
}
cout<<ans<<endl;
return 0;
}
字典序最小问题(poj3617)
给定长度为n的字符串s,不断选取当前S两端较小的一个拼接到空字符串T中,求出T(即字符串T的字典序尽可能地小)
同样地不断选取每个时刻S两端(s与s反转字符)较小的一个即可
while(a<=b)
{
bool left=false;
for(int i=0;a+i<=b;++i)
{
if(S[a+i]<S[b-i])
left=true;
else if(S[a+i]>S[b-i])
flag=false;
}
if(left)getchar(a++);
else getchar(b--);
}
putchar('\n');
霍夫曼编码(Huffman)
根据使用频率来最大化节省字符(编码)的存储空间。
出现的频率越高,在树中的深度就越深。
是当码字长度为整数时最优的编码方案。