Painting Fence题解

来源:互联网 发布:mac倍速看视频 编辑:程序博客网 时间:2024/05/01 13:55

codeforces里题目链接:http://codeforces.com/problemset/problem/448/C

要翻墙...



这道题一看题目,应该一开始就想到DP,然后复杂度O(n2),这个平方复杂度我就没去细想怎么实现了。

然后就是想有没有更好的方法,复杂度更低。


很容易能想到,如果某一列没有竖刷,那么必然被横刷,那么一定要从低处往高处都要横刷。所以如果整个区域有横刷,那么高度最矮的那一列一定要被完全横刷掉。

此时,这个区域除掉这些被横刷的这几行,上面的部分被分成好多堆。每一堆的求解跟原问题是一样的,所以可以分治(递归)求解。

上面这个方法就要不断找最小值,所以复杂度应该可以被压缩到nlog(n)。或者最小值不用找,直接快排一下,然后从小到大处理,复杂度nlog(n)。


nlog(n)的话,再考虑能否优化到O(n)。想到上面的log(n)花费花在求最小值上面,考虑是否有方法避免不求最小值,或者说,能否不按从小到大的顺序进行求解公式。


每一列,往左边数第一个小于它高度的位置记为index_left,往右边数第一个小于它高度的位置记为index_right。从而[index_left,index_right]可以看成一堆进行求解,而不对其它造成影响。 所以该题可以利用单调栈的思路来求解。

基本公式如下:


构建一个递增的单调栈,然后按公式进行求解,第一种情况,result赋给result_right,第二种,result赋给result_left

当然,上面只是基本公式,还有一些细节要处理才算严格正确。比如,相等高度的情况等等。


虽然代码中for循环里有while循环,但实际复杂度确实为O(n)。代码如下:

#include <stdio.h>#define mymaxsize 5001int myresult[mymaxsize];     //初始值为0int A[mymaxsize], index[mymaxsize]; //, index_right[mymaxsize]//, index_left[mymaxsize],index_right 记录右边第一个小于它的数的index  index_left 记录左边第一个小于它的数的indexint result_left[mymaxsize], result_right[mymaxsize];//int same_num[mymaxsize];int n, i,tmp, num = 1;<pre name="code" class="cpp">int tmp1, tmp2;
int main(){scanf("%d", &n);// A[0] = 0;// index[0] = 0;scanf("%d", &A[1]);index[1] = 1;// result_left[1] = 0;// index_left[1] = 0;for (i = 2; i <= n; ++i){scanf("%d", &tmp);if (A[num] >= tmp){num_left = num - 1;result_right[num] = 0;while (A[num_left] >= tmp){tmp1 = result_left[num] + result_right[num] + A[num] - A[num_left];tmp2 = i - index[num_left] - 1;if (tmp1 < tmp2) { result_right[num_left] = tmp1; }else { result_right[num_left] = tmp2; }--num;--num_left;}if (A[num]==tmp){result_left[num] = result_left[num] + result_right[num];}else{tmp1 = result_left[num] + result_right[num] + A[num] - tmp;tmp2 = i - index[num_left] - 1;A[num] = tmp;if (tmp1<tmp2) { result_left[num] = tmp1; }else { result_left[num] = tmp2; }}}else{A[++num] = tmp;result_left[num] = 0;}index[num] = i;}num_left = num - 1;result_right[num] = 0;while (num_left>=0){tmp1 = result_left[num] + result_right[num] + A[num] - A[num_left];tmp2 = i - index[num_left] - 1;if (tmp1 < tmp2) { result_right[num_left] = tmp1; }else { result_right[num_left] = tmp2; }--num; --num_left;}printf("%d", result_right[0]);return 0;}

 代码中有些数组其实可以不用,直接优化掉,比如result_right其实可以不用数组,直接用个变量来代替即可,不过不想改了。


网上似乎有看到dp解法,不过没去看是否正确,直接先扔在这:

#include <iostream>using namespace std;int main() {    int n; cin >> n;    int a[n+1];    a[0] = 0;    for (int i=1; i<=n; i++) cin >> a[i];    int dp[n+1][n+1];    for (int j=0; j<=n; j++) dp[n][j] = 0;    for (int i=n-1; i>=0; i--) for (int j=0; j<=i; j++) {    if (a[j] >= a[i+1]) dp[i][j] = dp[i+1][i+1]; //Already painted    else {        dp[i][j] = min( 1 + dp[i+1][j], a[i+1]-a[j] + dp[i+1][i+1]);    }    }    cout << dp[0][0] << endl;}


这道题有大神指导建议,可以用 笛卡尔树...比较直观好理解。嗯,不懂...以后再看吧,以后千万别成永远了...





0 0
原创粉丝点击