您的位置:首页 > 其它

HackerRank - almost-sorted-interval (思维)

2017-02-23 10:09 330 查看
题意:题意非常简单,给你n个数,范围都是1-n,问你这个序列中有多少个区间满足最小值在最左边,最大值在最右边

思路:想了一个下午。。好久没做题感觉要eat shit了。。

          假设我们从左往右扫过去,我们考虑维护两个序列以及它们的贡献值,一个为当前数字左边已合并区间的最小值,一个为当前数字左边已合并区间的最大值,设一个为s1,一个s2,那么比如我出一个样例为 1 2 4 3 5 

         合并的过程为,首先扫到1,把1添加进s1,s2,因为当前只有你一个所以最大最小值都是你了,当扫到2的时候,因为s1中的1比2小,所以最小值满足了条件,然后看最大值s2序列,也满足1<2,所以1和2是可以合并的,此时我们把序列s2中的1删除,放入2,因为考虑右边的数字往左合并的时候必须满足最大值要比2大,所以既然大于2,那么也就大于1,而对于s1序列中的1是不用删除的,因为考虑右边的数字往左合并的时候它可以合并到2这里,也可以合并到1这里,那么对于数字2的贡献值就要加上1的贡献值,此时答案就是1+2,
然后扫到4,由于同样满足上述的过程,所以此时s1有1,2,4 ,s2序列中有4,此时答案为1+2+3

           然后扫到3这个数字,由于3是比4要小的,所以3是显然不能和4合并在一起,所以我们要将4的贡献值-1,并且更新s1,把4弹出,放入3,同样的s2中的4要比3大,故不能合并,此时的s2应该有4,3两个元素,理由和s1中有多个的原因是雷同的,此时答案是1+2+3 +1 

   最后扫到5这个数字,由于满足3比5小,最小值满足条件,然后看s2中尾是3,满足3<5,故是可以合并的,此时对于5的贡献值就要加上3的贡献值,然后再看s2中的4,也满足4<5,故4和5也是可以合并在一起的,所以5的贡献值还要加上4的贡献值,此时答案为1+2+3+1+4 = 11 那么这道题大概的流程就是这样了,非常的神奇的一个O(n)的做法

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6+7;
#define LL long long
vector<int> mins;
vector<int> maxs;
int a[maxn];
int num[maxn];
LL ans = 0;
void maintainmins(int i)
{
if(mins.empty())
mins.push_back(a[i]);
else
{
int tmp = mins.back();
while(tmp > a[i])
{
num[maxs.back()]--;
if(!num[maxs.back()]) maxs.pop_back();
mins.pop_back();
if(mins.empty())break;
tmp = mins.back();
}
mins.push_back(a[i]);
}
}
void maintainmaxs(int i)
{
int tmp = maxs.back();
while(tmp < a[i])
{
num[a[i]]+=num[tmp];
maxs.pop_back();
if(maxs.empty())break;
tmp = maxs.back();
}
maxs.push_back(a[i]);
ans+=num[a[i]];
}
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<=n;i++)
num[i]=1;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i = 1;i<=n;i++)
{
maintainmins(i);
if(maxs.empty())
{
ans++;
maxs.push_back(a[i]);
continue;
}
maintainmaxs(i);
}
printf("%lld\n",ans);
return 0;
}



Almost sorted interval

 HackerRank - almost-sorted-interval 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: