题目大意:有一段深搜的代码,是遍历一个邻接矩阵,然后输出一个序列,这个邻接矩阵的原形是一棵树,那么现在就是要你根据序列,求出最多有多少个不同的树遍历之后可以得到相同的序列。
思路:这道题属于简单的区间DP,仔细点想就可以了。
第一种方法也是最直接的思路。
令dp[i][j]表示的是以i这个点为根,其余点为它的子树时,符合条件的最大个数。
从样例可以想到
1 2 3由于3和2交换之后,依然不会影响输出,所以假如1 2 xxx 3xxx,交换2,3节点下的树,也是不会影响序列的。
突破点就在此,dp[i][j]表示的便是一棵以i为根的(子)树,我们要做的就是去比较子树根的大小,判断可以不可以交换。
这边还需明白的一点在于:dp[i][j]表示以i为根的情况,dp[i-1][j]表示的便是i-j中的节点有任意多个节点在同一层的情况。(这个也不知道怎么讲- -!)
第一种方法的状态转移方程:dp[i][j]=dp[i][j]+dp[i+1][k]*dp[k][j](d[k+1]>d[i+1]),这边主要要理解的是不同子树的根能否位于同一层的情况。
AC代码:
#define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<cstdio> #include<cstring> using namespace std; long long dp[505][505]; int d[505]; #define mod 1000000007; /* dp[i][j]表示i为根时符合条件的情况,最直接的设想。 */ int main() { int n; while (cin >> n) { for (int i = 1; i <= n; i++) scanf("%d", &d[i]); memset(dp, 0, sizeof(dp)); for (int i = 1; i <= n; i++) dp[i][i] = dp[i][i + 1] = 1; for (int len = 2; len < n;len++) for (int i = 1; i + len <= n; i++) { int j = i + len; for (int k = i + 1; k <=j;k++) if (k==j||d[k+1]>d[i+1])//k==j是考虑最后一个顶点单独为一个第二层子树的情况 { dp[i][j] += dp[i + 1][k] * dp[k][j]; dp[i][j] %= mod; } } cout << dp[1][n] << endl; } }
第二种方法:
其实第二种方法跟第一种方法一样,只是表示上没那么直接
dp[i][j]表示的是i-j中的节点有任意多个节点在同一层的情况很明显这边的根便是i-1,并且先输出的是i,
所以最终的结果是dp[2][n+1]
AC代码:
/* dp[i][j] 表示的是i-j当中符合输出序列的树的个数,但需要注意的是 这边的i不一定为根,还包括i与i+1-j同一层的情况,需好好理解。 */ int main() { int n; while (cin >> n) { for (int i = 1; i <= n; i++) scanf("%d", &d[i]); memset(dp, 0, sizeof(dp)); for (int i = 1; i <= n + 1; i++) dp[i][i] = dp[i][i + 1] = 1; for (int len = 2; len <=n;len++) for (int i = 1; i + len <= n+1; i++) { int j = i + len; dp[i][j] += dp[i + 1][j];//i单独为节点的情况 for (int k = i + 1; k < j;k++) if (d[i] < d[k])//两者的差别在于判断条件。 { dp[i][j] += dp[i + 1][k] * dp[k][j];//因为i为节点,所以这个树有dp[i+1][k]种情况 dp[i][j] %= mod; } } cout << dp[2][n+1] << endl; } }
时间: 2024-10-12 12:12:56