正文

以空间换时间算法集锦(4)2007-04-20 14:42:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/goal00001111/25031.html

分享到:

5.士兵站队 题目:训练场上n(1≤n≤50000)个高矮都不相同的士兵从左到右排成一行,依次编号为1,2,…,n。 第i个士兵的身高H(i),由于采用特殊单位,H(i)满足1≤H(i)≤2000000000。 设第i个士兵右侧最近的比他个高的士兵编号为j,则第i个士兵可看到在他的右侧比他矮的士兵的个数S(i)=j-i-1。(不考虑客观因素,比如视力范围等-,-) 求S(1)+S(2)+…+S(n)。   输入: 标准输入。 第一行为整数n,表示士兵的个数。 第二行n个整数,用一个空格隔开。分别表示编号为1,2。。。n的士兵的身高   输出: S(1)+S(2)+…+S(n)的结果   例: 输入 6 10 3 7 4 12 2 输出 5     主函数: #include <iostream> #include <time.h>   using namespace std;   int Sum(int h[], int n);   int main(void) {     time_t startTime;     time_t endTime;     const int n = 50000;     int h[n] = {0};     int temp, p;     //获取互不相同的一组数列     for (int i=0; i<n; i++)         h[i] = i + 1;     for (int i=n-1; i>0; i--)//洗牌,使得数的大小随机排列     {         p = rand()%i;         temp = h[i];         h[i] = h[p];         h[p] = temp;     }     //计时     time(&startTime);     int s;     for (int i=1; i<10000; i++)         s = Sum(h, i);     time(&endTime);       cout << "sum = " << s << endl;     cout << "time = " << difftime(endTime, startTime) << endl;         system("pause");    return 0; } 方法一:根据题意可以得到一个很简单的算法,那就是遍历数组,依次累计每个元素右边比它小的元素,直到遇到比它大的元素为止。算法是时间复杂度为O(n^2)。 int Sum(int h[], int n) {     int sum = 0;     for (int i=0; i<n; i++)     {         for (int j=i+1; h[j]<h[i] && j<n; j++)             sum++;     }     return sum; } 或者 int Sum(int h[], int n) {     int i, j;     int sum = 0;     for (i=0; i<n; i++)//从左到右遍历每一个士兵     {         for (j=i+1; h[j]<h[i] && j<n; j++)//累计士兵右边比他矮的人数             ;         sum += j - i - 1;//累积总人数     }     return sum; }   方法二:一个分治算法,思路和方法一一样,都是遍历数组,求出每一个S(i),然后相加,区别在于不是从左到右依次计算,而是以当前的最高人为中心,分别计算其左右两边的人。算法是时间复杂度为O(n^2)。 int F(int h[], int low, int high) {     int i;     for (i=low+1; h[i]<h[low] && i<high; i++)//累计士兵右边比他矮的人数         ;     int sum = i - low - 1;     if (i > low + 1)         sum += F(h, low+1, i);     if (i < high-1)         sum += F(h, i, high);         return sum; }   int Sum(int h[], int n) {     F(h, 0, n); }   方法三:以空间换时间,设置一个临时数组来存储原数组中的递减序列,依次将原数组中的元素与序列中元素比较,看是否可以将其插入到序列中。插入的法则为:若h[i] > temp[0],即下一个士兵高度大于序列中任一高度,则用h[i]覆盖原序列中的所有元素,即top = 0;若h[i] < temp[top],即下一个士兵高度小于序列中任一高度,这样只要将h[i]插入到序列的尾部,即++top;若不是上述两种情况,即h[i]只比序列中的一部分元素大,这样只要用h[i]覆盖原序列中的比它小的元素,我们在序列中找到h[i]的插入位置就可以了。最后更新序列的元素,并累积序列的长度。这样时间复杂度可以降低到O(n)。 这里借鉴了http://www.programfan.com/club/的yxs0001的代码,表示感谢! int Insert(int h[], int n, int x)//二分法求插入的位置 {     int low = 0, high = n;     int m;         while (low <= high)//确保插入的位置是在low处,循环体最终总是执行low = m + 1;     {         m = (low + high) / 2;         if (h[m] > x)             low = m + 1;         else             high = m - 1;     }     return low; }   int Sum(int h[], int n) {     int *temp = new int[n];//设置一个临时数组来存储原数组中的递减序列     int top = 0;//临时数组栈顶     int sum = 0;     int i;         temp[top] = h[0];     for (i=1; i<n; i++)     {         if (h[i] > temp[0])//下一个士兵高度大于序列中任一高度             top = 0;         else if (h[i] < temp[top])//下一个士兵高度小于序列中任一高度             ++top;         else                     //下一个士兵高度在序列中将其插入序列             top = Insert(temp, top, h[i]);                 temp[top] = h[i];         sum += top;     }         delete []temp;         return sum; }   另外,http://www.programfan.com/club/的Kummerwu也写了一个很有趣的代码,他设置了一个结构,需要更多的空间,但是代码也更好理解,更高效。而且Kummerwu是一个很幽默的人,他幽默的注释,给程序带来了不少生趣。 typedef struct taller {     int h;      /* 第一个比自己高的士兵的身高 */     int pos;    /* 该士兵的位置 */ }TALLER;   //说白了,就是一个出栈,入栈的过程,每个元素入栈一次,在入栈的过程中, //可以将比自己小的元素踢出栈,所以最坏情况下,所有元素入栈,出栈各操作一次 //故复杂度为O(n) int Sum(int h[], int n) {     const int MAX_SOLDIER_CNT = 50000;     const int MAX_SOLDIER_H = 2000000000;     static TALLER talls[MAX_SOLDIER_CNT + 2]; //高个子俱乐部     TALLER * cur_tall = talls;     int count = 0;       cur_tall->h = MAX_SOLDIER_H + 1; //标兵(靠,这家伙还真高呀)     cur_tall->pos = n;         for (n-=2; n>=0; n--)     {         //如果我比右侧的那个家伙高(俺也算个高个子了)         if (h[n] > h[n+1])         {             //把比自己矮的士兵踢出栈,(呵呵,爽! )             while (cur_tall->h < h[n])             {                 cur_tall--; //这里可能有优化的空间??可以用二分法插入             }               //算算中间有几个矮冬瓜(现在cur_tall是第一个比我高的家伙)             count += cur_tall->pos - n - 1;               //呵呵,俺光荣的加入高个子俱乐部了 :).             cur_tall++;               cur_tall->h = h[n];             cur_tall->pos = n;         }         //靠,右侧那家伙比我高         //(算了,让他进入高个子堆吧,反正也没啥油水可以捞)         else         {             cur_tall++;             cur_tall->h = h[n+1];             cur_tall->pos = n+1;         }     }         return count; }

阅读(3480) | 评论(0)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

暂无评论
您需要登录后才能评论,请 登录 或者 注册