贪心法就是遵循某种规则,不断贪心地选取当前最优策略的算法设计方法。
1、硬币问题
题意
有1元、5元、10元、50元、100元、500元硬币各c1、c5、c10、c50、c100、c500枚,用这些硬币支付A元,最少需要多少枚硬币。至少存在一种支付方案。
思路
优先选择面值大的硬币。
代码
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int v[6]={1,5,10,50,100,500};//硬币面值
int c[6];
int A;//支付的钱数
void solve(){
int ans=0;
for(int i=5;i>=0;i--){
int t=min(A/v[i],c[i]);//使用硬币v[i]的枚数
A-=t*v[i];
ans+=t;
}
cout<<ans<<endl;
}
int main(){
for(int i=0;i<6;i++)
cin>>c[i];
cin>>A;
solve() ;
return 0;
}
2、区间问题
题意
有n项工作,每项工作分别在si时间开始,在ti时间结束。若参与一项工作,必须全程参与,参与工作的时间不能重叠。要想参与尽可能多的工作,求最多参与的工作的项数。
思路
优先选择结束时间早的工作。
代码
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 100000
int n,s[maxn],t[maxn],i;
pair<int,int>a[maxn];
void solve(){
//对pair进行字典排序
//为了让结束时间早的排在前面,把t[i]存入first,s[i]存入second
for(i=0;i<n;i++){
a[i].first=t[i];
a[i].second=s[i];
}
sort(a,a+n);//按结束时间从小到大排序
int ans=0,t=0;
for(i=0;i<n;i++){
if(t<a[i].second){
ans++;
t=a[i].first;
}
}
cout<<ans<<endl;
}
int main(){
cin>>n;
for(i=0;i<n;i++)
cin>>s[i];
for(i=0;i<n;i++)
cin>>t[i];
solve();
return 0;
}
3、字典序最小问题
题意
给定长度为N,只包含大写英文字母的字符串S,用它构造字符串T。起初T是空串,随后反复进行任意下列操作:
从S头部删除一个字符,加到T的末尾;
从S尾部删除一个字符,加到T的末尾。
使构造的T字典序尽可能小。
思路
不断取S的开头和结尾中较小的字符放到T的末尾。
代码
#include<stdio.h>
#include<iostream>
using namespace std;
#define maxn 2001
int N;
char S[maxn];
void solve(){
int a=0,b=N-1;
while (a<=b){
bool left=0;
for(int i=0;a+i<=b;i++){
if(S[a+i]<S[b-i]){
left=1;
break;
}else if(S[a+i]>S[b-i]){
left=0;
break;
}
}
if(left)
putchar(S[a++]);//a增
else
putchar(S[b--]); //b减
}
putchar('\n');//putchar输出单个字符
}
int main(){
cin>>N;
for(int i=0;i<N;i++)
cin>>S[i];
solve();
return 0;
}
4、其他例题
4.1、poj-3069 Saruman’s Army
题意
直线上有N个点,从中选出若干点标记,求最少标记的点数使每个点在距离为R以内的区域里必须有带标记的点。
思路
从最左边开始考虑,给右侧R距离以内最远的点标记,不断重复,直到覆盖所有的点。
代码
#include<stdio.h>
#include<iostream>
#include<algorithm>
#define maxn 1002
using namespace std;
int N,R,X[maxn];
void solve(){
sort(X,X+N);//从小到大排列
int i=0,ans=0;
while(i<N){
int s=X[i++];//s是没有被覆盖的最左边点的位置
while(i<N&&X[i]<=s+R)//一直向右直到距s的距离大于R的点
i++;
int p=X[i-1];//p是新标记点的位置
while(i<N&&X[i]<=p+R)
i++;
ans++;
}
cout<<ans<<endl;
}
int main(){
cin>>N>>R;//N个点 距离为R
for(int i=0;i<N;i++)
cin>>X[i];//每个点的位置
solve();
return 0;
}
4.2、poj-3253 Fence Repair
题意
将一块很长的木板切割成N块,每次切割木板的开销为这块木板的长度,求最小开销。
思路
对应切割方法用二叉树表示,叶子节点对应所需切割木板的长度,深度对应切割次数,则开销合计为各叶子节点的木板长度×节点的深度。
代码
#include<stdio.h>
#include<iostream>
#include<algorithm>
#define LL long long
#define maxn 20001
using namespace std;
int N,L[maxn];
void solve(){
LL ans=0;
while(N>1){//直到计算到木板为一块时为止
int m1=0,m2=1;//m1为最短的板,m2为次短的版
if(L[m1]>L[m2])
swap(m1,m2);
for(int i=2;i<N;i++){
if(L[i]<L[m1]){
m2=m1;
m1=i;
}else if(L[i]<L[m2]){
m2=i;
}
}
int t=L[m1]+L[m2];//将两块木板拼合
ans+=t;
if(m1==N-1)
swap(m1,m2);
L[m1]=t;
L[m2]=L[N-1];
N--;
}
cout<<ans<<endl;
}
int main(){
cin>>N;
for(int i=0;i<N;i++){
cin>>L[i];
}
solve();
return 0;
}