【CodeForce】509F Progress Monitoring(树形情景区间DP)

题目大意:有一段深搜的代码,是遍历一个邻接矩阵,然后输出一个序列,这个邻接矩阵的原形是一棵树,那么现在就是要你根据序列,求出最多有多少个不同的树遍历之后可以得到相同的序列。

思路:这道题属于简单的区间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

【CodeForce】509F Progress Monitoring(树形情景区间DP)的相关文章

Codeforces 509F Progress Monitoring (区间dp 或 记忆化搜索)

F. Progress Monitoring time limit per test 1 second memory limit per test 256 megabytes Programming teacher Dmitry Olegovich is going to propose the following task for one of his tests for students: You are given a tree T with n vertices, specified b

Codeforces 509F Progress Monitoring 给定dfs序求树的同构数 区间dp

题目链接:点击打开链接 ==说同构数有点不对..反正就是这个意思,对于某个点的所有儿子,先访问标号小的,再访问标号大的. dp[l][r]表示 区间[l,r] 构成一棵树的方法数. 对于一个区间[l, r] 构成一棵树,则点l一定是根,然后枚举2个区间相乘即可 dp[l][r] = dp[l+1][i] * dp[i+1][r] ( i = [l+1, r] ) 当然 a[i+1] > a[l+1] ,这样才会满足题目中的暴力代码. import java.io.PrintWriter; imp

Codeforces 509F Progress Monitoring

http://codeforces.com/problemset/problem/509/F 题目大意:给出一个遍历树的程序的输出的遍历顺序b序列,问可能的树的形态有多少种. 思路:记忆化搜索 其中我们枚举第一个子树的大小,然后后面的其他子树可以继续分解. 1 #include<cstdio> 2 #include<cmath> 3 #include<algorithm> 4 #include<cstring> 5 #include<iostream&

codeforce #505D - Recovering BST 区间DP

1025D 题意: 有一个递增序列,问能不能构建出一颗每条边的端点值都不互质的二叉排序树. 思路: 区间DP,但是和常见的区间DP不一样, 这里dp[i][j]表示的是区间[i,j]能否以i为根建立一个小二叉排序树. 所以可以得到dp[i][j] 为true, 要求在[i+1,j]中有一个k,dp[k][i+1]和dp[k][j]都为true. 或者在i点的左边取件中,即要求在[j][i-1]中有一个k,dp[k][j]和dp[k][i-1]都为true. #include <algorithm

lightoj1025_区间dp

题目链接:http://lightoj.com/volume_showproblem.php?problem=1025 题目描述: 给出一个字符串,可以任意删除位置的字符,也可以删除任意多个.问能组成多少个回文串? 解题思路: 自从开始学dp,感觉自己智商一直处于离线状态.席八啊啊啊啊啊啊!今天随机到这个题目,看了好久竟然没有看出来是区间DP.知道是区间DP后立马感觉明白. 情景设定 dp[l][r] 表示 区间 [l, r] 内的回文串数目. 状态转移:dp[l][r] = dp[l][r-1

【HDU】5151 Sit Sit Sit(区间DP+排列组合)

题目大意:可以查阅网站的中文翻译,就不说了,是第24场Best Coder 思路: 这道这道区间DP,我也开始觉得其实区间DP是一种应用型的思想,做这类题目一个重要的点是在于题目情景的把握,这道题的一个情景就是数学的排列组合问题. 首先应用根据小区间推出大区间的思路,我们可以先固定一个位置k,k位置是最后做的位置,那么我们要算出在这种情况下符合的方法数,假如k是头或者尾,那不用说, 此时的方法为dp[i+1][j]或者是dp[i][j-1]; 假如k是有邻居的,那我们就要考虑邻居的颜色,如果颜色

uva 10003 Cutting Sticks 简单区间dp

// uva 10003 Cutting Sticks 区间dp // 经典的区间dp // dp(i,j)表示切割小木棍i-j所需要的最小花费 // 则状态转移为dp(i,j) = min{dp(i,k) + dp(k,j) + a[j]-a[i]) // 其中k>i && k<j // a[j] - a[i] 为第一刀切割的代价 // a[0] = 0,a[n+1] = L; // dp数组初始化的时候dp[i][i+1]的值为 0,这表示 // 每一段都已经是切割了的,不

黑书例题 Fight Club 区间DP

题目可以在bnuoj.soj等OJ上找到. 题意: 不超过40个人站成一圈,只能和两边的人对战.给出任意两人对战的输赢,对于每一个人,输出是否可能是最后的胜者. 分析: 首先序列扩展成2倍,破环成链. dp[i][j]表示i和j能够相遇对打,那么dp[i][i+n]为真代表可以成为最后胜者. 枚举中间的k,若i和j都能和k相遇,且i和j至少一人能打赢k,那么i和j可以相遇. 复杂度o(n^3) 1 #include<cstdio> 2 #include<cstring> 3 usi

算法复习——区间dp

感觉对区间dp也不好说些什么直接照搬讲义了2333 例题: 1.引水入城(洛谷1514) 这道题先开始看不出来到底和区间dp有什么卵关系···· 首先肯定是bfs暴力判一判可以覆盖到哪些城市····无解直接输出···有解得话就要想想了···· 这道题关键是要发现··如果一个蓄水池所在城市可以覆盖到一些沙漠城市···那么这些沙漠城市肯定是一段····不然假设有一个城市是断开的而两边都被同一个蓄水池流出的水覆盖,这个城市四周的城市都肯定比它矮···(不理解举个反例吧···反正我举不出来)···然后就