Educational Codeforces Round 83 (Rated for Div. 2) E - Array Shrinking ( 区间DP O(n^3) || 栈+DP O(n^2) )

挂机一个多小时都没想出来这道题,不过好在前四题切的快还是上分了

看到有人说这题是区间DP裸题...我怀疑我要回炉再造了

两种解法,一种是O(n^3)的区间DP

先预处理哪些段可以合并成为一个数字,复杂度n^3,再区间DP

f(i, j) = min(f(i, k) + f(k + 1, j)) ( i <= k < j)

#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 510;
int a[N], num[N][N], f[N][N];
int main() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        num[i][i] = a[i];
    }
    for (int len = 1; len < n; len++)
        for (int l = 1; l <= n - len; l++) {
            int r = l + len;
            f[l][l] = 1;
            for (int i = l; i < r; i++)
                if (num[l][i] == num[i + 1][r] && num[l][i] > 0)
                    num[l][r] = num[l][i] + 1;
            if (num[l][r] > 0)
                f[l][r] = 1;
            else
                f[l][r] = r - l + 1;
        }
    f[n][n] = 1;
    for (int len = 1; len < n; len++)
        for (int l = 1; l <= n - len; l++) {
            int r = l + len;
            for (int i = l; i < r; i++)
                f[l][r] = min(f[l][r], f[l][i] + f[i + 1][r]);
        }
    printf("%d", f[1][n]);
    return 0;
}

 

但是其实有更美观复杂度更低的做法(学长教我的

用栈把预处理优化到n^2,然后用d[i]表示前i个最少能变成几个,递推式就变成了

d[i] = d[j - 1] + 1 (if i ~ j可以合并成一个)

#include <cstdio>
#include <algorithm>
#include <stack>
using namespace std;
const int N = 510;
int a[N], d[N];
int main() {
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
		scanf("%d", &a[i]);
	for (int i = 1; i <= n; i++) {
		stack < int > s;
		d[i] = d[i - 1] + 1;
		s.push(a[i]);
		for (int j = i - 1; j > 0; j--) {
			if (s.empty() || s.top() != a[j])
				s.push(a[j]);
			else {
				int u = a[j];
				while (!s.empty()) {
					if (u != s.top())
						break;
					u = s.top() + 1;
					s.pop();
				}
				if (s.empty())
					d[i] = min(d[i], d[j - 1] + 1);
				s.push(u);
			}
		}
	}
	printf("%d", d[n]);
	return 0;
}

  

posted @ 2020-03-11 13:39  cminus  阅读(204)  评论(0)    收藏  举报