尝试在http://www.hackerearth.com/problem/algorithm/sum-of-medians-1/解决问题,并考虑使用multiset解决问题,因为它可能包含重复值。 我尝试编码如下:
#include<iostream>
#include<set>
#include<algorithm>
using namespace std;
int main()
{
int n,k,med_sum=0,p;
cin>>n;
multiset<int> m;
multiset<int>::iterator itr;
for(int i=0;i<n;i++)
{
cin>>k;
m.insert(k);
p=k;
if(p<=k)
itr--;
else
itr++;
med_sum+=*itr;
}
cout<<med_sum<<endl;
return 0;
}
Sample Input:
n=5
10
5
1
2
15
Sample Output: 27
Explanation:
m(1)=median of [10]=10
m(2)=median of [10 5]=5
m(3)=median of [10 5 1]=5
m(4)=median of [10 5 1 2 15]=5
med_sum=10+5+5+2+5=27
答案 0 :(得分:6)
一种简单的方法是维护两个已分类的容器,一个低于中位数,一个更大。
当你找到一个新元素时,看看要插入哪个容器(这样下面的所有元素总是小于或等于上面的所有元素),然后重新平衡计数,使中位数“在间隙中”他们之间。
您可以这样做,但将较低范围定义为[.begin(), it)
,将较高范围定义为[it, .end())
。除非性能特别重要,否则我会将它们设置为单独的容器,以减少需要保持头脑以理解代码的状态。
使用以下不变量维护两个已排序的容器low
和high
:
low.size()
与high.size()
或1更大low
的每个元素都小于或等于high
的每个元素。
然后,low
union high
的中位数为low.last()
。假设你有这样一对容器,如果你想添加一个元素x
,我会首先保持不变2 - 如果low.empty()
或x <= low.last()
,请将其粘贴在{ {1}},否则请将其粘贴在low
。
接下来,保持不变1:如果high
包含的元素多于低,请从high
中移除最低元素并将其添加到high
。如果low
的元素多于low
,则删除high
中的最高元素,并将其添加到low
。
如果我们从遵循上述两个不变量的high
和low
开始,那么在完成上述步骤后,我们仍然有high
和low
服从这两个不变量不变量。当上述两个不变量成立时,中位数为high
!
答案 1 :(得分:3)
您似乎尝试做的事情(错误地,使用您发布的代码)将值保存在多重集中,迭代器指向中间值,并在每次添加到集合时调整该指针,具体取决于是否您要添加的值高于或低于中位数。
这是一个很好的设计,可能是解决这个问题的最快方法。但是,它不适用于std::multiset
,因为std::multiset
的限制会导致一个关键角落问题 - 当添加到集合中的新值等于旧的中位数时,无法知道它是在多重集中的旧中位数之前还是之后插入的。所以你不知道在这种情况下如何调整中值指针。
因此,如果您想以这种方式解决问题,您需要以一种可以为您提供这一重要信息的方式实现您自己的rbtree或其他集合抽象。或者,当插入一个等于旧中位数的值时,你可以从头开始重新计算中位数(这是一项昂贵的操作,但应该很少见。)
修改强>
提示OP使代码适用于C ++ 11多字节:
将第一个值插入多集时,需要初始化itr
您只需在itr
的“错误”一侧插入后调整itr
。你可以根据多重大小是奇数还是偶数来判断哪一方是“错误的”。
因为itr
您想要测试if (k < *itr)
而不是<=
答案 2 :(得分:1)
使用std::vector
怎么样?通过插入std::lower_bound
告诉您的位置来保持值的排序。
std::vector<int> p;
int k, med_sum;
while(cin >> k)
{
p.insert(std::lower_bound(p.begin(), p.end(), k), k);
med_sum += p[p.size()/2];
}
std::cout << med_sum << std::endl;
修改强>
使用向量的优点是算法非常清晰简洁(只有3行逻辑)和vector
内存的缓存局部性以及大容量内存分配使其至少具有竞争力{ {1}} / map
或set
实施,即使其插入为list
。
为了进行比较,我使用O(n)
编写了一个(未经测试的)实现:
multiset
7行逻辑并不坏,但它肯定不太清楚发生了什么,我不能仅仅通过观察它是否正确。请注意,此算法仅适用于 std::multiset<int> p;
int k, med_sum;
cin >> med_sum;
p.insert(med_sum);
std::multiset<int>::iterator itr = p.begin();
while(cin >> k)
{
if(p.size() % 2 == 0)
{
if(k < *itr) ++itr;
}
else
{
if(k < *itr) --itr;
else ++itr;
}
med_sum += *itr;
}
cout << med_sum << endl;
,其中C++11
在范围的顶端(重复项)插入重复值。
总而言之,我认为multimap::insert
实施是更好的方法,除非必要性和衡量标准与vector