贪心小记
贪心策略是一种在每次决策是采取当下意义下最优策略的算法。贪心算法,要满足由局部最优解可以推出全局最优解。简单来说,就是把一个很大的问题拆成一个个子问题,然后找出每个子问题的最优解,组合成全局最优解
贪心问题总是只看眼前,并不考虑以后可能造成的影响 。因此在选择贪心算法时,一定要考虑它的正确性。
举个不正确的贪心:noip2011提高组day2第3题观光公交,当年考试时的数据用贪心可以100pts(具体思路可以看题解),然鹅实际上贪心是错的,被别人卡掉了qwq;
贪心的常见做法:
在提高组难度以下的题目中,最常见的贪心有两种。一种是:「我们将 XXX 按照某某顺序排序,然后按某种顺序(例如从小到大)处理」。另一种是:「我们每次都取 XXX 中最大/小的东西,并更新 XXX」,有时「XXX 中最大/小的东西」可以优化,比如用优先队列维护。
排序法:
用排序法常见的情况是输入一个包含几个(一般一到两个)权值的数组,通过排序然后遍历模拟计算的方法求出最优值。
后悔法:
eg:洛谷p2949工作调度
贪心思想:
1 . 先假设每一项工作都做,将各项工作按截止时间排序后入队。
2 . 在判断第 i 项工作做与不做时,若其截至时间符合条件,则将其与队中报酬最小的元素比较,若第 i 项工作报酬较高(后悔),则 ans+=a[i].p-q.top()。
PS :用优先队列(小根堆)来维护队首元素最小。
贪心问题说简单也简单,说难也难,主要在于证明想到的贪心是正确的。
下面举几个贪心算法的经典应用:
1.选择不相交区间问题:
给定n个开区间(ai,bi),选择尽量多个区间,使得这些区间两两没有公共点。
【思路】按照结束时间bi从小到大排序,依次考虑每个事件,如果不会重复,那么就可以选择这个事件。
eg:活动安排
将活动按结束时间排序,排序后从第一个扫到最后一个,如果与前面选到的不冲突就选择这个事件
#include<bits/stdc++.h> using namespace std; int n; struct node{ int st,ed; }a[1005]; bool cmp(node x,node y){ return x.ed<y.ed; } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d",&a[i].st,&a[i].ed); sort(a+1,a+n+1,cmp); int t=a[1].ed; int ans=1; for(int i=2;i<=n;i++){ if(a[i].st>=t){ ans++; t=a[i].ed; } } cout<<ans<<endl; }
2.区间选点问题:
给定n个闭区间[ai,bi],在数轴上选尽量少的点,使得每个区间都至少有一个点(不同区间内含的点可以是同一个);
【思路】按结束bi从小到大排序,对于区间,若没有点,则加一个点在队尾(因为队尾是最可能被重复覆盖的地方);
eg:种树
#include<cstdio> #include<algorithm> #include<iostream> using namespace std; int n,m,k,ans; bool p[50100]; struct h{ int b,e,t; }a[50100]; bool cmp(h x,h y){ return x.e<y.e; } int main(){ cin>>n>>m; for(int i=1;i<=m;i++) cin>>a[i].b>>a[i].e>>a[i].t; sort(a+1,a+m+1,cmp); for(int i=1;i<=m;i++){ k=0; for(int j=a[i].b;j<=a[i].e;j++) if(p[j]) k++; if(k>=a[i].t) continue; for(int j=a[i].e;j>=a[i].b;j--) if(!p[j]) { p[j]=1; k++; ans++; if(k==a[i].t) break; } } cout<<ans; }
3.区间覆盖问题:
之前讲过了qwq?就是引水入城的线段覆盖中的一种吧qwq
eg:喷水装置
#include<bits/stdc++.h> using namespace std; int n,cnt,l,h,x,r; struct seg{double x,y;}a[20005]; bool cmp(const seg &x,const seg &y){ return x.x<y.x; } void read(){ cin>>n>>l>>h; cnt=0; for(int i=1;i<=n;i++){ cin>>x>>r; if(r<=h/2) continue; cnt++; a[cnt].x=x-sqrt(r*r-h*h/4.0); a[cnt].y=x+sqrt(r*r-h*h/4.0); } } void solve(){ double t=0; int ans=0,bj=1,i=1; while(t<l){ ans++; double s=t; for(;a[i].x<=s&&i<=cnt;i++) if(t<a[i].y) t=a[i].y; if(t==s&&s<l) { cout<<"0"<<endl; bj=0; break; } } if(bj) cout<<ans<<endl; } int main(){ int t; cin>>t; while(t--){ read(); sort(a+1,a+cnt+1,cmp); solve(); } }