加分二叉树
又是熟悉的遍历题……好了又不会了。
这是一道很神奇的题,虽然可以称为树形DP但是它不需要建树!
首先,因为中序遍历每个连续的一段都对应一棵子树,所以我们完全可以使用区间DP的方法去把小区间合并成大区间来计算。
使用dp[i][j]表示区间i,j之内(也就是一棵子树)的最高加分。特殊的,dp[i][i-1]表示一棵空子树,其权值为1。
这样的话转移方程就是dp[i][j] = max(dp[i][j],dp[i][k-1] * dp[i][k+1] + dp[k][k]),如果这个值较大发生了更新,就直接把它设置为这段区间的根节点。
这样我们求成功求出了最大值。至于前序遍历很好办,我们递归访问就可以了。
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<queue> #include<set> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar('\n') using namespace std; typedef long long ll; const int M = 1000005; const int INF = 1e9+7; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } int dp[100][100],v[100],n,root[105][105]; void print(int l,int r) { if(l > r) return; if(l == r) { printf("%d ",l); return; } printf("%d ",root[l][r]); print(l,root[l][r]-1); print(root[l][r]+1,r); } int main() { n = read(); rep(i,1,n) v[i] = read(); rep(i,1,n) dp[i][i] = v[i],dp[i][i-1] = 1; per(i,n,1) rep(j,i+1,n) rep(k,i,j) { if(dp[i][j] < (dp[i][k-1] * dp[k+1][j] + dp[k][k])) dp[i][j] = dp[i][k-1] * dp[k+1][j] + dp[k][k],root[i][j] = k; } printf("%d\n",dp[1][n]); print(1,n); return 0; }
当你意识到,每个上一秒都成为永恒。