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

来源:互联网 发布:java 唤醒sleep的线程 编辑:程序博客网 时间:2024/04/29 22:50


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 by its adjacency matrix a[1... n, 1... n]. What is the output of the following pseudocode?


used[1 ... n] = {0, ..., 0};

procedure dfs(v):
    print v;
    used[v] = 1;
    for i = 1, 2, ..., n:
        if (a[v][i] == 1 and used[i] == 0):
            dfs(i);

dfs(1);
In order to simplify the test results checking procedure, Dmitry Olegovich decided to create a tree T such that the result is his favorite sequence b. On the other hand, Dmitry Olegovich doesn't want to provide students with same trees as input, otherwise they might cheat. That's why Dmitry Olegovich is trying to find out the number of different trees T such that the result of running the above pseudocode with T as input is exactly the sequence b. Can you help him?

Two trees with n vertices are called different if their adjacency matrices a1 and a2 are different, i. e. there exists a pair (i, j), such that 1 ≤ i, j ≤ n and a1[i][j] ≠ a2[i][j].

Input
The first line contains the positive integer n (1 ≤ n ≤ 500) — the length of sequence b.

The second line contains n positive integers b1, b2, ..., bn (1 ≤ bi ≤ n). It is guaranteed that b is a permutation, or in other words, each of the numbers 1, 2, ..., n appears exactly once in the sequence b. Also it is guaranteed that b1 = 1.

Output
Output the number of trees satisfying the conditions above modulo 109 + 7.

Sample test(s)
input
3
1 2 3
output
2
input
3
1 3 2
output
1


题目链接:http://codeforces.com/problemset/problem/509/F

题目大意:给一个序列b[i],问通过题目所给伪代码深搜出来遍历路径和序列b[i]相同的树有多少种,结果对1e9 + 7取余

题目分析:两种姿势区间dp和记忆化搜索,其实原理都一样。

先说记忆化搜索解法:

由于题目说了b1肯定等于1,也就是根确定,因为要按照1,2...n的顺序深搜,我们可以取一段区间,开始就是1到n,进行深搜,我们按照增序来找

1.因为根为1即b[1]=1,b[i]又是一个排列,所以b[2]必然大于1,我们直接从b[2]开始往后比,当满足b[l + 1] < b[i + 1]时,子树可以继续分解,分解成两部分,显然一部分把l+1当作根,另一部分把i当作根,根据乘法原理

ans = (ans + DFS(l + 1, i) * DFS(i, r))

2.当i == r时,最右端的端点可以单独成一棵子树,例如4   1 2 4 3这个3可以单独当作右子树,而4   1 4 3 2我们不能把3 2部分单独放在右子树,因为这样必有和4在同一层的,4肯定后访问了,如果l==r则说明找到了一组子树情况

#include <cstdio>#include <cstring>#define ll long longint const MAX = 505;int const MOD = 1e9 + 7;ll dp[MAX][MAX];int b[MAX];ll DFS(int l, int r) {    ll ans = dp[l][r];    if(ans >= 0)        return ans;    if(l == r)         return 1;    ans = 0;  //表示每一个子树的合法方案数    for(int i = l + 1; i <= r; i++)        if(i == r || b[l + 1] < b[i + 1])            ans = (ans + DFS(l + 1, i) * DFS(i, r)) % MOD;      dp[l][r] = ans % MOD;    return ans % MOD;}int main() {    int n;    scanf("%d", &n);    for (int i = 1; i <= n; i++)        scanf("%d", &b[i]);    memset(dp, -1, sizeof(dp));    int res = DFS(1, n);    printf("%d\n", res);}


区间dp解法:

其实就是把记忆化搜索的递归改成递推,就是区间dp咯,dp[i][j]表示以i为根到j位置这个区间构成的子树的方案数。

转移方程:(i == r || b[l + 1] < b[i + 1])  i: [l + 1, r]  dp[l][r] = dp[l][r] + dp[l + 1][i] * dp[i][r] ,可以发现与递归一样,不过速度显然递推快

#include <cstdio>#include <cstring>#define ll long longint const MOD = 1e9 + 7;int const MAX = 505;int b[MAX];ll dp[MAX][MAX];int main(){    int n;    scanf("%d", &n);    for(int i = 1; i <= n; i++)        scanf("%d", &b[i]);    memset(dp, 0, sizeof(dp));    for(int i = 1; i <= n; i++)        dp[i][i] = 1ll;    for(int len = 1; len < n; len++)      {          for(int l = 1; l + len <= n; l++)          {              int r = l + len;              for(int i = l + 1; i <= r; i++)                 if(i == r || b[l + 1] < b[i + 1])                      dp[l][r] = (dp[l][r] + dp[l + 1][i] * dp[i][r]) % MOD;          }      }      printf("%I64d\n", dp[1][n]);}



0 0
原创粉丝点击