這一題可能貪心的策略有不少,好比以左端從小到大排序,按順序選取知足條件的區間。或者以右端從小到大排序。node
以左端從小到大排序能夠找到反例說明可能形成有短區間浪費沒用到的狀況。函數
而以右端點爲何就不會呢?(這裏給一個個人解釋,但只是直觀解釋,沒有嚴格的數學證實)spa
可能會形成短區間的浪費,而以右端點排序就會首先考慮段區間,因此綜合考慮以右端點排序的方式更優。3d
這樣右邊剩餘的空間最大,能夠有更多的區間提供選擇。code
(關於用右端點排序最優的數學證實我尚未想出來,若是哪位大佬知道但願能夠指導我一下)blog
對於區間是否選擇以及cur的更新:排序
假設當前區間能夠移動的最大距離爲mid, 那麼某個區間能夠移動的範圍爲 [ l - mid, r + mid ],知足這個條件的區間可使用,與cur有以下四種關係:ci
綜合以後 ①②爲一種狀況,③④爲一種狀況。數學
題目要求最小,那麼 x==y==z/2,因此處理時只須要將每一個區間都乘以2,最後判斷結果是否能被2整除。it
#include<cstdio> #include<vector> #include<algorithm> using namespace std; //輸入 int n; struct node//區間 { int l; int r; node(int l1,int r1){ l = l1 ; r = r1;}//構造函數 }; vector<node> v; bool cmp(node n1, node n2) { if( n1.r==n2.r ) return n1.l<n2.l;//若是右端點相等,選擇更接近cur的 return n1.r<n2.r;//以右端點從小到大排序 } bool C(int mid)//判斷mid是否知足條件 { int cur = 0; vector<node> v_(v); while( true ) { bool flat = false;//有沒有知足條件的區間 for(int i=0; i<v_.size(); i++) { int l = v_[i].l, r = v_[i].r; if( l-mid<=cur&&cur<=r+mid )//知足條件 { if( cur-l>=mid )//狀況 1,2 { //cur += mid + r - cur; cur = mid+r; } else//狀況 3,4 { cur += (r-l);// cur += len } v_.erase(v_.begin()+i);//去掉此區間 flat = true; break;//每次找到一個區間cur的狀態都改變,原來不能移動的區間如今可能能夠移動 } } if( !flat || cur>=20000 ) break;//若是沒有知足條件的區間||已經覆蓋了全部區間 } return cur>=20000; } void solve() { sort(v.begin(),v.end(),cmp); int l = 0, r = 20000; while( r-l>1 )//二分法求解 { int mid = (l+r)/2; if( C(mid) ) r = mid;//知足條件,縮小移動距離 else l = mid; } if( r%2==0 ) printf("%d\n",r/2); else printf("%.1f\n",(float)r/2.0); } int main() { scanf("%d",&n); while( n-- ) { int l,r; scanf("%d%d",&l,&r); v.push_back(node(2*l,2*r));//每一個區間乘以2 } solve(); return 0; }