Ch5302 金字塔

金字塔


Description

链接
虽然探索金字塔是极其老套的剧情,但是有一队探险家还是到了某金字塔脚下。经过多年的研究,科学家对这座金字塔的内部结构已经有所了解。首先,金字塔由若干房间组成,房间之间连有通道。如果把房间看作节点,通道看作边的话,整个金字塔呈现一个有根树结构,节点的子树之间有序,金字塔有唯一的一个入口通向树根。并且,每个房间的墙壁都涂有若干种颜色的一种。
探险队员打算进一步了解金字塔的结构,为此,他们使用了一种特殊设计的机器人。这种机器人会从入口进入金字塔,之后对金字塔进行深度优先遍历。机器人每进入一个房间(无论是第一次进入还是返回),都会记录这个房间的颜色。最后,机器人会从入口退出金字塔。
显然,机器人会访问每个房间至少一次,并且穿越每条通道恰好两次(两个方向各一次), 然后,机器人会得到一个颜色序列。但是,探险队员发现这个颜色序列并不能唯一确定金字塔的结构。现在他们想请你帮助他们计算,对于一个给定的颜色序列,有多少种可能的结构会得到这个序列。因为结果可能会非常大,你只需要输出答案对10^9 取模之后的值。
样例


Solution

区间 dpdp

容易想到设dp[l,r]dp[l, r]表示[l,r][l,r]的方案数, 当Sl=SrS_l = eq S_r时, 显然dp[l,r]=0dp[l, r] = 0
阶段状态已经为[l,r][l, r], 那么考虑怎么 划分区间, 即 决策

一段区间即表示一颗子树, 而这棵子树可能包含多颗更小的子树, 即可能划分为多个子区间(子问题),
则可以在这段区间中找到合法的分界点kk, 使得Sl=Sk=SrS_l = S_k = S_r, 将这个区间划分为[l+1,k1],[k,r][l+1, k-1], [k, r]这两个区间,
也可以理解为: 将[l+1,k1]  (k[l+2,r])[l+1, k-1] (k ∈ [l+2, r]) 作为第一颗子树, 然后[k,r][k, r]作为另外一部分子树, 由于每次第一颗子树大小不等, 可以保证计数 不重不漏.

而当 Si==SjS_i == S_j时, 可以使得dp[i,j]=dp[i+1,j1]dp[i, j] = dp[i+1, j-1], 表示以SiS_i为根的只有一个儿子的子树

遵循以上信息, 可以得出 dp[i,j]dp[i, j]表示区间[i,j][i, j]组成的子树的方案数, 其中SlSrS_l和S_r为进入和出去产生的字符,
状态转移方程自然也就出来了

dp[i,j]={1              (i==j)0              (Si  !=Sj    i>j)dp[i+1,j1]+k=l+2r2dp[i+1,k1]dp[k,r]                (Si==Sj)dp[i, j] = egin{cases} 1 (i == j)\ 0 (S_i != S_j || i > j)\ dp[i+1, j-1] + sum_{k=l+2}^{r-2}dp[i+1, k-1]*dp[k, r] (S_i == S_j) \ end{cases}
使用递归实现即可


Attention

上方的第三个状态转移方程在程序中写成
dp[i,j]=k=l+2rdp[i+1,k1]dp[k,r]                (Si==Sj)dp[i,j] =sum_{k=l+2}^{r}dp[i+1, k-1]*dp[k, r] (S_i == S_j)

这是因为k=r1k=r-1时, =dp[i+1,r2]dp[r1,r]=0右 = dp[i+1, r-2] * dp[r-1, r] = 0

k=rk=r时, =dp[i+1,r1]dp[r,r]=dp[i+1,r1]右 = dp[i+1, r-1]*dp[r, r] = dp[i+1, r-1]


Code

#include<bits/stdc++.h>
#define reg register

const int maxn = 305;
const int mod = 1e9;


char S[maxn];
int N;
int dp[maxn][maxn];

int Dp(int l, int r){
        if(l > r) return 0;
        if(l == r) return 1;
        if(S[l] != S[r]) return dp[l][r] = 0;
        if(~dp[l][r]) return dp[l][r];
        dp[l][r] = 0;
        for(reg int k = l+2; k <= r; k ++) dp[l][r] = (1ll*dp[l][r] + 1ll * Dp(l+1, k-1) * Dp(k, r)) % mod;
//        if(S[l] == S[r]) dp[l][r] += dp[l+1][r-1]==-1?0:dp[l+1][r-1];
        return dp[l][r];
}

int main(){
        freopen("pyr.in", "r", stdin);
        freopen("pyr.out", "w", stdout);
        scanf("%s", S+1);
        N = strlen(S+1);
        memset(dp, -1, sizeof dp);
        Dp(1, N);
        printf("%d
", dp[1][N]);
        return 0;
}
原文地址:https://www.cnblogs.com/zbr162/p/11822644.html