搜索算法和动态规划是在多种策略中选取最优解,贪心算法是遵循某种规则,不断地选取当前最优策略。
一、硬币问题
有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 }
二、区间问题
有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 }
三、字典序最小问题
给定长度为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 }
四、
直线上有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 }
五、
农夫约翰为了修理栅栏,要将一块很长的木板切割成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 }
然后可以联想到霍夫曼编码(当码字长度为整数情况时最优的编码方案)。频度较高的字母对应较短的01序列,频度较低的字母对应较长的01序列。将木板换成字符,长度换成频度可以类比。