CF1312E Array Shrinking 题解 区间DP+DAG上的DP
题目链接:https://www.luogu.com.cn/problem/CF1312E
解题思路:
本题要做两次DP。
第一次DP:区间DP。
定义 \(f[i][j]\) 表示 \([i,j]\) 范围内的数合并成一个数之后的这个数是啥;如果没有办法合并成一个数,则 \(f[i][j] = -1\)。
第二次DP:DAG上的DP。
如果 \(f[i][j] \ne -1\),则从 \(i-1\) 到 \(j\) 连一条长度为 \(1\) 的边,求点 \(0\) 到点 \(n\) 的最短路,因为是DAG,所以直接DP求解(\(dp[i]\) 表示的就是 \(0 \rightarrow i\) 的最短路长度)。
示例代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 505;
int n, a[maxn], f[maxn][maxn], dp[maxn];
bool vis[maxn][maxn];
int dfs(int L, int R) {
if (vis[L][R]) return f[L][R];
vis[L][R] = true;
f[L][R] = -1;
if (L == R) return f[L][R] = a[L];
for (int i = L; i < R; i ++) {
int tmp1 = dfs(L, i);
int tmp2 = dfs(i+1, R);
if (tmp1 != -1 && tmp1 == tmp2) {
if (f[L][R] == -1 || f[L][R] > tmp1+1) {
f[L][R] = tmp1 + 1;
}
}
}
return f[L][R];
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i ++) scanf("%d", a+i);
memset(dp, 0x3f, sizeof(dp));
dp[0] = 0;
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= i; j ++)
if (dfs(j, i) != -1)
dp[i] = min(dp[i], dp[j-1]+1);
printf("%d\n", dp[n]);
return 0;
}