一、贪心
1、概述
贪心是一种思想,指的是在对问题进行求解时,总是做出在当前看来是最好的选择,不从整体最优加以考虑,而是从局部最优考虑。
遇到最优解问题,凡是能通过“局部最优”最后推出“全局最优”的问题,都可以使用贪心。
2、贪心策略
贪心策略需满足无后效性,即当前的状态不会影响到已经发生过的状态,后发生的事件不能改变之前先发生事件的最优解。
贪心策略是否可行,需要证明。我们可以用微扰法,范围缩放,反证法,决策包容性等方面判断当前贪心方法是否可行。
3、例题
①P1199 [NOIP2010 普及组] 三国游戏 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
解析:
当我们选择某一个武将时,机器一定会选择与此武将匹配默契值最高的另一个。所以对于小涵来说最大只能取与此武将匹配默契值第二高的自由武将。
小涵和电脑都拿不到每个武将对应配对的最大默契值,因为小涵每拿起最大组合的其中一个,另一个就被电脑拿走了,但是电脑也无法占有该组合的最大值,因为另一个在小涵手里,这个组合的最大值其实就作废了。
但是小涵在第二次选将时可以拿到第一次选中的武将的次大默契值组合。这样,小涵只需要拿所有次大中最大的就能保证赢。
代码:
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> using namespace std; int a[510][510]; int main() { int n; scanf("%d",&n); for(int i=1;i<n;++i) { for(int j=i+1;j<=n;++j) { scanf("%d",&a[i][j]); a[j][i]=a[i][j]; } } int ans=0; int cnt=0; for(int i=1;i<=n;++i) { sort(a[i]+1,a[i]+n+1); ans=max(ans,a[i][n-1]);//选出次大的 } printf("1\n");//一定能赢 printf("%d",ans); return 0; }
②P1007 独木桥 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
解析:
独木桥上如果只有一个士兵,那么最长时间和最短时间就很好求。
士兵遇到后转头且不耗时间,那么只看士兵的话,因为走过的路程是不变的,那么其实等效于两个士兵都没掉头,都是直接走过去的。
按照这个思路,士兵间是没有影响的。最长时间就是所有士兵最长时间中的最大值,最短时间就是所有士兵最短时间中的最大值。
代码:
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> using namespace std; int main() { int n,l; int maxx=0; int minn=0; scanf("%d%d",&l,&n); for(int i=1;i<=n;++i) { int k; scanf("%d",&k); maxx=max(maxx,max(k,l-k+1));//最大 minn=max(minn,min(k,l-k+1));//最小 } printf("%d %d",minn,maxx); return 0; }
③P1223 排队接水 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
解析:
排在第 i 人之后的人,所有人的等待时间都要再加上第 i 人的接水时间,那我们安排第 i 人是接水时间较短的人就可以使之后的人排队时间尽可能少。所以我们从花费时间最小的人开始接水即可。
代码:
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; struct node { int id,val; //序号和接水时间 }a[1000010]; bool cmp(node a,node b) { return a.val<b.val; } int main() { int n; scanf("%d",&n); for(int i=1;i<=n;++i) { scanf("%d",&a[i].val); a[i].id=i; } sort(a+1,a+n+1,cmp);//排序,按照接水时间从小到大 double sum=0; for(int i=1;i<=n;++i) { printf("%d ",a[i].id); sum+=a[i].val*(n-i); } sum/=n; printf("\n%.2lf",sum); return 0; }
P1090 [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
解析:
将两堆合并为一堆最省力的方法为挑两堆最小的进行合并,如果每次都挑最小的两堆,就可以做到消耗体力总和最小。
由于每次合并都改变了最小两堆的位置,我们可以用priority_queue优先队列存储数据,便于快速找出最小的两堆。
代码:
#include<cstdio> #include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<queue> using namespace std; int main() { priority_queue<int,vector<int>,greater<int> > q; int n; scanf("%d",&n); for(int i=1;i<=n;++i) { int a; scanf("%d",&a); q.push(a); } int ans=0; for(int i=1;i<=n-1;++i) { int min1,min2,hh; min1=q.top(); q.pop(); min2=q.top(); q.pop(); hh=min1+min2;//最小的两堆合并 ans=ans+hh; q.push(hh); } printf("%d ",ans); return 0; }
二、结构体排序
sort与结构体
1.在sort中加入cmp辅助函数
struct node { int id,val; }a[N]; bool cmp(node a,node b) { return a.val>b.val;//按val从大到小排序 } sort(a+1,a+n+1,cmp);
2.利用lambda函数将cmp嵌入sort中
struct node { int id,val; }a[N]; sort(a+1,a+n+1,[](node a,node b) { return a.val>b.val;//按val从大到小排序 });
重载运算符
我们自己规定的结构体大小判断依据
struct node { int id,val; bool operator <(const node b) const { return val<b.val; }//重载结构体的小于号 bool operator >(const node b) const { return val>b.val; }//重载结构体的大于号 //还可以重载 == <= >= != 运算符 //用到哪个重载哪个 }a[N]; //另一种写法,写在结构体外,不推荐 bool operator <(const node a,const node b) { return a.val<b.val; }
这样,我们可以直接比较两个结构体
struct node { int id,val; }; node a[N]; if(a[l]<a[r]) printf("%d",a[r].val); sort(a+1,a+1+n);
还可以将结构体直接放入一些数据结构如priority_queue中,同样是用到哪个重载哪个,不知道的全来一遍。
priority_queue <node> a;//找大 priority_queue <node,vector<node>,greater<node> > a;//找小