制定参赛顺序
贪心策略:
如果己方最低的也比我方最高的高,就让我方最低的去,否则在能获胜的情况下选取我方分数大于其第一个去。
小技巧:可以利用二叉搜索树(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;
}