贪心专题技巧
貪心專題
- 制定參賽順序
- 調度問題
- 加熱便當
- 米那斯雅諾
制定參賽順序
貪心策略:
如果己方最低的也比我方最高的高,就讓我方最低的去,否則在能獲勝的情況下選取我方分數大于其第一個去。
小技巧:可以利用二叉搜索樹(mutiset)在保存我方的,因為在保存的時候,會默認由低到高排序,
代碼:
int order(const vector<int>&russion,const vector<int>&korean){int n = russion.size(),wins = 0;multiset<int>ratings(korean.begin(),korean.end());for(int rus = 0;rus<n;++rus){if(*ratings.rbegin()<russion[rus])ratings.erase(ratings.begin());else{ratings.erase(ratings.lower_bound(russion[rus]));++wins;}}return wins; }調度問題
加熱便當
等到快中午的時候元碩才發現,營地里只有1臺可供加熱的微波爐。更糟糕的是,這臺微波爐功率太小,每次只能加熱1個便當。假設,加熱第i個便當需要耗費mi秒,吃這個便當將耗費ei秒的時間。
求最小的吃午飯時間
貪心策略
無論以何種方式最后的時間總是,所以的便當加熱時間加上其中一個吃便當的時間,這是因為最后加熱的便當只能最后被吃掉,即使之前有一個吃的時間很長,那么就是加這個之前的。
因為我們以 吃便當的時間進行降序排列。遍歷一篇即可。
代碼:
int e[N],m[N]; int heat(){//決定加熱順序vector<pair<int,int>>order;for(int i=0;i<n;++i)order.push_back(make_pair(-e[i],i)); // 負數,那么大的序號就會自動雜前邊sort(order.begin(),order.end());//模擬整個過程int ret = 0,beginEat = 0;for(int i=0;i<n;++i){int box = order[i].second; //得到序號beginEat += m[box];ret = max(ret,beginEat+e[box]);} }米那斯雅諾
題意:
米那斯雅諾城從來沒有被攻陷過的,這里有一個半徑為8公里的巨大的圓形城墻——拉馬斯安澈。圍繞整個城市的巨大城墻中設有n個哨所,各哨所的監視范圍為以哨所為中心、半徑為r:的圓。不過,因城墻結構限制,各個哨所的設立點并不規則,而且能夠監視的范圍也各不相同。
圖中,租實線表示城墻,五星表示哨所,虛線則表示哨所的監視范圍。為了以最少兵力監視整個城墻,僅想在一部分哨所上安排哨兵。給出各個哨所的位置和監視范圍時,設計一個程序計算監視整個城墻所需的最少兵力。
為了簡便起見,假設城墻是厚度為0的圓形,而哨所是一個點。
先通過轉換,將問題轉換為圓心角區間的問題,要監視所以的圓形部分,就要判斷出監視領域的并集能否完全覆蓋[0,2pi)
const double pi = 2.0 * acos(0); int n; double y[100],x[100],r[100]; pair<double,double>ranges[100];void convertToRange(){for(int i =0;i<n;++i){double loc = fmod(2*pi + atan2(y[i],x[i]),2*pi);double range = 2.0 * asin(r[i]/2.0/8.0);ranges[i] = make_pair(loc - range,loc + range);}//默認會以起始位置最小的區間開始排序sort(ranges,ranges); }fmod()函數:
頭文件:#include< cmath >
fmod()用來對浮點數進行取模(求余)。設x=k*n+h,則返回值為h(h和x的符號相同)。
貪心求解
const int INF = 987654321; int n; pair<double,double>ranges[100]; // 選擇覆蓋0點的區間后,剩余的展成線段int solveCircular(){int ret =INF;//按起始位置升序排列各區間sort(ranges,ranges+n);//選擇覆蓋0點的區間后for(int i=0;i<n;++i)if(ranges[i].first<=0||ranges[i].second>=2*pi){// 排除被此區間覆蓋的部分后,剩余的圓心角區間double begin = fmod(ranges[i].second,2*pi);double end = fmod(ranges[i].first+2*pi,2*pi);ret = min(ret,1+solveLinear(begin,end))} }int solveLinear(double begin,double end){int used = 0,idx = 0;//只要有未覆蓋的線段就一直執行while(begin<end){//在begin 之前的起始區間中求出離右端點最近的區間double maxCover = -1;while(idx < n&& ranges[idx].first <= begin){maxCover = max(maxCover,ranges[idx].second);++idx;}//未找到能夠覆蓋的區間時if(maxCover <= begin)return INF;//去掉已被覆蓋的線段begin = maxCover;++ used;}return used; }總結