POJ 2559 Largest Rectangle in a Histogram
题目见此:http://poj.grids.cn/practice/2559/
多种解法,经典好题啊!
解法一:
解题思路:
- 这题的基本思想是找出每一个矩形的左右边界(即左右边第一个比他高的),但如果直接对每一个矩形暴搜会超时。
- 于是可以dp一下:如果它原有左边界的左边第一个,比他本身还要高,那么它原有左边界的左边第一个的左边界就是它的左边界。晕了吧~看代码:
tmp = i; //先把左边界设为自己 while(arr[i] <= arr[tmp-1] && tmp > 1) tmp = left[tmp-1];
右边界同理
- 还要注意一点,就是tmp > 1 这句一定不能缺,表示如果左边界到了最左边,那么就是它了。
贴代码:
1 #include <stdio.h> 2 #include <string.h> 3 #include <algorithm> 4 #include <iostream> 5 using namespace std; 6 int n; 7 int a[100010], l[100010], r[100010]; 8 9 int main() 10 { 11 while(scanf("%d", &n), n) 12 { 13 memset(a, 0, sizeof(a)); 14 for(int i=1 ; i<=n ; i++) 15 { 16 scanf("%d", &a[i]); 17 l[i] = r[i] = i; 18 while(l[i] > 1 && a[l[i]-1] >= a[i]) 19 l[i] = l[l[i]-1]; 20 } 21 long long res = 0; 22 for(int i=n ; i>=1 ; i--) 23 { 24 while(r[i] < n && a[r[i]+1] >= a[i]) 25 r[i] = r[r[i]+1]; 26 res = max((long long)(r[i]-l[i]+1)*a[i], res); 27 } 28 cout << res << endl; 29 } 30 }
注意几点:
- 首先这道题要用到long long
- 这题花费我时间最长的地方不是写代码,而是第28行输出最后结果!!!POJ貌似有bug啊,用printf("%ld", res)和printf("%_I64d", res)都会WA,这……调了我半个小时
POJ上的数据为:
Accepted |
1572kB | 90ms | 553 B | G++ |
总结:
自己写代码的速度太慢了,思路也要半天才能想出来,还是做题太少,另外很少有一次写对的时候,每次都要调半天……一定要多练题,多做dp,争取期末考好!
解法二:
在网上看到了第二种解法 使用单调队列,思路很好,也很好写,http://blog.sina.com.cn/s/blog_71fda43501010sbb.html
这个博客是我见到的这种解法里写的最好的,再次不赘述了
贴代码:
1 #include <iostream> 2 #include <stdio.h> 3 #include <algorithm> 4 using namespace std; 5 6 long long n, h[100001], r[100001], l[100001], pos[100001], val[100001]; 7 int main() 8 { 9 int head, tail; 10 while(scanf("%ld", &n), n) 11 { 12 head = 0, tail = -1; 13 for(int i=1 ; i<=n ; i++) 14 { 15 scanf("%ld", &h[i]); 16 l[i] = 1; 17 r[i] = n; 18 while(head <= tail && h[i] < val[tail]) 19 { 20 r[pos[tail]] = i-1; 21 --tail; 22 } 23 pos[++tail] = i; 24 val[tail] = h[i]; 25 } 26 head = 0, tail = -1; 27 long long int res = 0; 28 for(int i=n ; i>=1 ; i--) 29 { 30 while(head <= tail && h[i] < val[tail]) 31 { 32 l[pos[tail]] = i+1; 33 --tail; 34 } 35 pos[++tail] = i; 36 val[tail] = h[i]; 37 } 38 for(int i=1 ; i<=n ; i++) 39 res = max(res, (r[i] - l[i] + 1)*h[i]); 40 cout << res << endl; 41 } 42 }
值得注意的是:
不能把第38行和第39行并入到28行的循环中去,因为r是动态更新的,不是一次更新就不变了
POJ上的数据为:
Accepted |
3144kB | 50ms | 766 B | G++ |
时间复杂度明显更优,但空间复杂度增加
解法三:
在看http://www.2cto.com/kf/201207/140484.html博客时看到了一个更好的解法,总体思路是单调栈
这篇博客里也解释的很详细,不再赘述
贴代码:
1 #include <stdio.h> 2 #include <iostream> 3 using namespace std; 4 #define max(a,b) a > b ? a : b 5 #define N 100005 6 int q[N]={-1},w[N]; //w记录的是从这个点开始,之前有几个高度大于等于此高度. 7 int main() 8 { 9 int n,h; 10 while(scanf("%d",&n),n) 11 { 12 int top = 0; 13 long long ans = 0; 14 for(int i=1;i<=n+1;i++) 15 { 16 if(i != n+1) 17 scanf("%d",&h); 18 else 19 h = 0; 20 if(h > q[top]) 21 q[++top] = h , w[top] = 1; 22 else 23 { 24 long long cnt = 0; 25 while(h <= q[top]) 26 { 27 ans = max(ans ,(cnt+w[top])*q[top] ); 28 cnt += w[top--]; 29 } 30 q[++top] = h; 31 w[top] = cnt+1; 32 } 33 } 34 cout << ans << endl; 35 } 36 return 0; 37 }
POJ上的数据为:
Accepted |
512kB | 50ms | 932 B | G++ |
虽然时间跟单调队列一样,但空间省了不少,很强大的方法
更多解法:室友说还可以用斜率优化……不过有点杀鸡用牛刀了,而且数据也不会比以上解法更优