mooc程序设计(三)第九周第三题
重复是麻烦的一点,
emm这道题emm一开始本着面向对象的思想想要用set来按实力值和id来保存用户的信息,而且想要保存所有的用户信息,所以写了个这样的代码:
#include<set>
#include<iostream>
#include<algorithm>
using namespace std;
class Fighter
{
public:
int id;
int point;
Fighter (int a,int b):id(a),point(b){}
};
class lessthan//我们一般用函数对象传入模板作为类型参数,同时在运算符或者比较的使用的时候,最好尽量少些,防止语义重复,
//比如运算符重载和自己写的比较函数容易弄混,同时也应当注意stl中这些容器传入的标准参数中那些比较函数的参数,比如这里的lessthan
//本来默认调用less,但是我们没有重载运算符,所以不如自己写一个函数对象。
{
public:
bool operator() (const Fighter A,const Fighter Ano)
{
if(A.point < Ano.point) return true;
else if(A.point==Ano.point&&A.id<Ano.id)
return true;
else
return false;
}
};
class equivalent//为了find_if而准备
{
public:
int point;
bool operator ()(Fighter A)
{
return A.point==point;
}
equivalent(Fighter A)
{
point=A.point;
}
};
//要把程序逻辑想清楚,这道题有重复之后问题比较复杂,遇到两个重复的值是难点
int main(void)
{
int n;
int id,point;
multiset<Fighter,lessthan> all;
typedef Fighter F;
all.insert(F(1,1000000000));
multiset<Fighter,lessthan>::iterator low,up,ifeq;
//用文件比较调试;
cin>>n;
while(n--)
{
cin>>id>>point;
F f(id,point);//之所以创建了一个fighter对象就是因为自己写的比较函数仅限于fighter之间,所以在比较和后续函数中传递的value
//都应当是fighter类型的值,
ifeq=find_if(all.begin(),all.end(),equivalent(f));
if(ifeq==all.end())//没有重复的该值
{
low=all.lower_bound(f);
if(low!=all.begin()) low--;
low=find_if(all.begin(),all.end(),equivalent(F(0,low->point)));//这里要注意如果差值绝对值相等,但是小的有许多相等值需要重新定位一个最小的lowerbound
up=all.upper_bound(f);
if(up==all.end()) up--;//up恰好是id比较小的那个
}
else //有重复的值,这里注意 ,lowerbound 和upperbound 并不能满足要求,如果有重复的要选id最小的,但是lowerbound返回的反而是比较大的那个
{
up=low=find_if(all.begin(),all.end(),equivalent(f));
}
if((point-low->point) !=(up->point-point))
{
cout<<id<<" "<<((point-low->point) <(up->point-point)?low->id:up->id)<<endl;
}
else
{
cout<<id<<" "<< (up->id>low->id?low->id:up->id) <<endl;
//从low开始有小于等于point的一直到up之前
}
all.insert(f);
}
return 0;
}
一开始灰常天真地以为只需要区别和point值有没有重复就可以了,后来发现了一个一直漏掉的问题:如果有实力值相差绝对值相同的两个人我们可以比较这两个人的id,可是问题是,在这里low的那个可能不是唯一一个实力值与新手差值等于up那个的,up本身在set中的顺序就保证了up就是id最小且实力恰好大的那个,但low就不一定了,可能有id小于low的且实力等于low的,所以要再找一下,,,,,
但是修了这个bug之后,特么时间超出限制,果然要ac就必须选择性丢失用户信息,,,,
那么问题能够稍微简单一点:我们用map存用户信息,实力在前,id在后,每次新用户判断其实力值是不是有重复的,如果有那么把id较小的插进去,(也就是说老用户拜拜喽)
emmm,然后过了,,,,,,
#include<iostream>
#include<map>
#include<utility>
using namespace std;
//map中比较显著的特点就是一一映射,同时我们的排序最好都基于key的值来完成,当然综合上对value的比较也可以
//在用stl之前,建议去查一下函数和方法的源码,这里总是吃不看源码的亏,,,于是人为写了好多运算符重载,整个人都不好了,,,
//真的让人难受,这道题做了好多次,,,
int main(void)//不如还是仅存不重复的用户数据,每次如果有重复的
{
int id,point;
int n;
map<int,int> all;
pair<int,int> _n(1000000000,1);
all.insert(_n);
pair<int,int> one;
map<int,int>::iterator ifeq,low,up;
cin>>n;
while(n--)
{
cin>>id>>point;
one.first=point,one.second=id;
ifeq=all.find(one.first);//这里用algorithm的find会出错,因为algorithm的find中是用==号来寻找对应的值的。
if(ifeq==all.end())//不存在有相同poin0t值的老用户
{
low=all.lower_bound(one.first);
if(low!=all.begin()) low--;
up=all.upper_bound(one.first);
if((one.first-low->first)!=(up->first-one.first))
{
cout<<one.second<<" "<<((one.first-low->first)<(up->first-one.first)?low->second:up->second)<<endl;
}
else cout<<one.second<<" "<<(low->second < up->second?low->second:up->second)<<endl;
all.insert(one);
}
else
{
cout<<one.second<<" "<<ifeq->second<<endl;//直接输出,并且把id较小的那个存到map里
ifeq->second = (ifeq->second < one.second ? ifeq->second:one.second);
}
}
return 0;
}