【 貪心 進階總結 】【 來自一本通提高篇 】
阿新 • • 發佈:2019-02-15
求區間與區間的最大不覆蓋數—套板子(區間右端點從小到大排序,選擇不衝突的,儘可能的選)簡記為線段覆蓋問題
求區間內最少單點滿足覆蓋要求,不管是各個區間需要一個點還是一個子區間(這個子區間是可連續、可不連續的)
按照區間右端點從小到大排序,依次滿足各個區間的要求,如此下來種的樹(需要的點)最少。
如果想不出貪心,線段樹+優化亦可作。
選擇儘量少的區間覆蓋指定線段區間。
【 模板 】將所有的區間按照左端點從小到大排序,對於當前查詢的起點s ,選擇覆蓋點 s 的區間中右端點中最大的一個,並將 s 更新為此區間的右端點,(這樣區間數目+1)直到s>=整個線段長度。(在s時刻,找一個左端點小於等於s的最大右端點)
對於這道題,需要進行一些轉化——每個澆灌噴頭受限於所能噴到的最短距離,它的最短距離是半徑到達草坪上端。此時,如果將整個草坪長度想象為 x軸的正半軸,那麼,我們應該根據勾股定理找到水平距離,即()。把當前點的pos減去水平距離作為左端點,pos+水平距離作為右端點。然後就是套上面的板子↑。
PS:一定要理解了過程再寫,一定要理解了過程再寫,一定要理解了過程再寫!!!
附上AC程式碼:
#include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #define N 300005 using namespace std; const double Eps = 1e-5 ; inline int wread(){ char c=getchar ();int flag=1,wans=0; while (c<'0'||c>'9'){if (c=='-') flag=-1;c=getchar ();} while (c>='0'&&c<='9'){wans=wans*10+c-'0';c=getchar ();} return wans*=flag; } int T; int n,L,W; struct node {double lx,rx;int pos,r;}a[N]; bool e666 (node x,node y){return x.lx<y.lx;} int main (){ T=wread(); while (T--){ memset (a,0,sizeof a); n=wread();L=wread();W=wread(); for (int i=1;i<=n;++i){ a[i].pos=wread(),a[i].r=wread(); if (a[i].r*a[i].r-(W*1.0/2.0)*(W*1.0/2.0)<0) a[i].pos=-1,a[i].lx=-1,a[i].rx=-1; //對於不能覆蓋完草坪上端的一個圓(例如樣例1中的那個夾在草坪中間的圓) 肯定是不會選它的 else { a[i].lx=max (0.000000,(double)a[i].pos-sqrt((double)a[i].r*(double)a[i].r-(W*1.0/2.0)*(W*1.0/2.0))); a[i].rx=a[i].pos+sqrt((double)a[i].r*(double)a[i].r-(W*1.0/2.0)*(W*1.0/2.0)); } } sort (a+1,a+n+1,e666); int nx=1; double s=0;//起點 bool F=true;//判斷是否能覆蓋完整個區間 int ans=0;//記錄 while (nx<=n){ ans++; double re=s; while (a[nx].lx<=re && nx<=n){//模板 if (a[nx].rx>=re) s=max(s,a[nx].rx); nx++; } if (s>=L) {break;} if (re==s) {F=false;break;}//未更新,請畫圖____即當前區間與下個區間之間 一定有一段距離,不符合全部覆蓋的要求,退出 } if (!F) puts("-1"); else printf("%d\n",ans); } return 0; }
未完待續...