求一道ACM题不超时的解法

lzr4304061988012 2009-05-26 11:24:17
有一排若干个具有一定高度的牛,设每一个牛到比他高的最近的牛的距离是d,求这些d之和的平均值;
测试数据:
牛的个数为7
各牛高度是数列7 6 5 8 6 4 10

输出分析:
这些d值分别为3 1 1 3 1 1 7
均值为(3+1+1+3+1+1+7) / 7 = 2.43;

本人算法比较菜,虽然算法有所改进,但是仍旧超时,希望有人提供高效率的算法,感觉不难。
...全文
193 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
shanchailee 2009-05-27
  • 打赏
  • 举报
回复
呵呵,这是上周湖南大学比赛的题目吧。
我也参加了,当时总是超时,很郁闷~~
后来想了想,可以用栈
先设一个数组d,用来保存各个牛的最大相邻较高牛距离
正向遍历读入的高度串1...n
若此高度h[i]比栈顶的高,就压i入栈
若此高度h[i]比栈顶的低,就把栈中所有的元素弹出,根据弹出的各高度值的索引k和当前高度索引i的差值计算d[k]
如此一直到串尾
然后反向遍历高度串,进行同样的操作,必要时更新d[k]
最后对d数组的个元素求平局值即可
:)
liao05050075 2009-05-27
  • 打赏
  • 举报
回复
一个简单的DP。
设left[i]表示在第i只牛左边比它高并离它最近的编号,height[i]表示i牛的高度
那么对于每只牛j,做
int now=j-1;
while(height[now]<=height[j]) now=left[now];
从左到右做一遍。
同理,也有right数组,
从右到左做一次。

然后对于每只牛i,d[i]=min{left[i],right[i]};

这个做法是近似O(n)的,虽然有看似有for,然后又while,但事实上这样跳着向前是很快的。

北大OJ上也很多用到这个方法的题目,
3250Bad Hair Day 是用这个方法做的题目中最容易的一个
lzr4304061988012 2009-05-27
  • 打赏
  • 举报
回复
谢谢LS!
liao05050075 2009-05-27
  • 打赏
  • 举报
回复
首先,不要用cin,不然在数据很大的时候,你光输入就超时了。
再次,用vector的话,最也先reserve一下,申请一个最大可能的空间。能不vector话也不要用,毕竟它没有数组来得快。
再次,数组你能不要动态申请就不要动态申请。

ps.或者你把题目网址贴出来,我有空去做做看
lzr4304061988012 2009-05-27
  • 打赏
  • 举报
回复
顶一下
shanchailee 2009-05-27
  • 打赏
  • 举报
回复
呵呵,我没试过,你可以在湖大的OJ上试一下。应该没有问题
lzr4304061988012 2009-05-27
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 shanchailee 的回复:]
呵呵,这是上周湖南大学比赛的题目吧。
我也参加了,当时总是超时,很郁闷~~
后来想了想,可以用栈
先设一个数组d,用来保存各个牛的最大相邻较高牛距离
正向遍历读入的高度串1...n
若此高度h[i]比栈顶的高,就压i入栈
若此高度h[i]比栈顶的低,就把栈中所有的元素弹出,根据弹出的各高度值的索引k和当前高度索引i的差值计算d[k]
如此一直到串尾
然后反向遍历高度串,进行同样的操作,必要时更新d[k]
最后对d数…
[/Quote]
2L成功A了吗?
lzr4304061988012 2009-05-27
  • 打赏
  • 举报
回复
晕,用1L的方法依然超时了;


#include <iostream>
#include <vector>
#include <cmath>
#include <cstdio>
using namespace std;

double SortCow(int * h,int * left,int * right,int n)
{

int i;

for(i = 1;i<n;i++)
{
int now = i-1;
while(now != n && h[now] <= h[i])
now = left[now];
if(now != n)
left[i] = now;
}

double sum = 0;
if(left[n-1] != n)
sum+=n-1-left[n-1];
else
sum+=n;
for(i =n-2;i>=0;i--)
{
int now = i+1;
while(now != n && h[now] <= h[i])
now = right[now];
if(now != n)
right[i] = now;

if(right[i] != n && left[i]!=n)
sum += (i-left[i])<(right[i]-i)?(i-left[i]):(right[i]-i);
else if(left[i] == n && right[i] == n)
sum += n;
else if(left[i] == n && right[i] !=n)
sum += right[i]-i;
else
sum+= i-left[i];

}

return sum;

}
int main()
{
int n;
vector<double> re;
while(cin>>n)
{
int * h = new int[n];
int * left = new int[n];
int * right = new int[n];

int i;
for(i = 0;i<n;i++)
{
cin>>h[i];
left[i] = right[i] = n;
}
re.push_back(SortCow(h,left,right,n)/n);
delete[] h;
delete[] left;
delete[] right;
}
vector<double>::iterator it;
for(it = re.begin();it!=re.end();it++)
printf("%.2f\n",*it);
return 0;
}
绿色夹克衫 2009-05-27
  • 打赏
  • 举报
回复
看来正反各遍历一次的问题,大家是能够达成共识的。

感觉2楼说的用栈作的方法,可能会存在一些问题。

还是1楼的方法好,其中的left[now]有点像kmp中的next的回溯部分,这样来说确实是O(n)的方法!

33,010

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧