Acwing 284.金字塔 (区间DP)
题面
虽然探索金字塔是极其老套的剧情,但是有一队探险家还是到了某金字塔脚下。
经过多年的研究,科学家对这座金字塔的内部结构已经有所了解。
首先,金字塔由若干房间组成,房间之间连有通道。
如果把房间看作节点,通道看作边的话,整个金字塔呈现一个有根树结构,节点的子树之间有序,金字塔有唯一的一个入口通向树根。
并且,每个房间的墙壁都涂有若干种颜色的一种。
探险队员打算进一步了解金字塔的结构,为此,他们使用了一种特殊设计的机器人。
这种机器人会从入口进入金字塔,之后对金字塔进行深度优先遍历。
机器人每进入一个房间(无论是第一次进入还是返回),都会记录这个房间的颜色。
最后,机器人会从入口退出金字塔。
显然,机器人会访问每个房间至少一次,并且穿越每条通道恰好两次(两个方向各一次), 然后,机器人会得到一个颜色序列。
但是,探险队员发现这个颜色序列并不能唯一确定金字塔的结构。
现在他们想请你帮助他们计算,对于一个给定的颜色序列,有多少种可能的结构会得到这个序列。
因为结果可能会非常大,你只需要输出答案对109 取模之后的值。
输入格式
输入仅一行,包含一个字符串S,长度不超过300,表示机器人得到的颜色序列。
输出格式
输出一个整数表示答案。
输入样例:
ABABABA
输出样例:
5
思路
刚看到这个题目的时候还以为是和树有关的遍历问题,但没想到是个区间dp。首先,读完题目后我们就可以发现一些性质,这个字母序列的首尾一定相同因为会回到根节点,那么根据树的递归特性我们可以发现,每一棵子树都会具有这个性质,换句话说,对于一棵树或者子树的遍历,我们得到的字母序列左右端点字母一定相同。然后,一棵树的形态我们很容易把它区分为左子树形态数乘右子树的形态数,这样来来看的话区间dp的形式就比较明显了,然后我们套用框架,枚举长度和起点,哦,对,还有一些细节的点,我们发现对一棵树的遍历序列必定是奇数,所以我们在枚举区间的时候也应该遍历奇数。通过这道题目,对区间dp或者说是dp问题有了一个更加深刻的认识,其实dp做法就是优雅的暴力枚举,而和大暴力不同的是,dp做法挖掘了状态与状态的潜在关系,以及一些限制条件,从而优化了暴力,本质还是枚举了各种的状态,已达到最终状态的目的。
代码实现
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
#define rep(i,f_start,f_end) for (int i=f_start;i<=f_end;++i)
#define per(i,n,a) for (int i=n;i>=a;i--)
#define MT(x,i) memset(x,i,sizeof(x) )
#define rev(i,start,end) for (int i=0;i<end;i++)
#define inf 0x3f3f3f3f
#define mp(x,y) make_pair(x,y)
#define lowbit(x) (x&-x)
#define MOD 1000000007
#define exp 1e-8
#define N 1000005
#define fi first
#define se second
#define pb push_back
typedef long long ll;
typedef pair<int ,int> PII;
typedef pair<int ,PII> PIII;
ll gcd (ll a,ll b) {return b?gcd (b,a%b):a; }
inline int read() {
char ch=getchar(); int x=0, f=1;
while(ch<'0'||ch>'9') {
if(ch=='-') f = -1;
ch=getchar();
}
while('0'<=ch&&ch<='9') {
x=x*10+ch-'0';
ch=getchar();
} return x*f;
}
const int maxn=310;
const int mod=1e9;
string s;
int f[maxn][maxn];
int main () {
cin>>s;
int n=s.size ();
if (n%2==0) puts("0");
else {
rep (len,1,n) {
for (int l=0;l+len-1<n;l++) {
int r=len+l-1;
if (l==r) f[l][r]=1;
else if (s[l]==s[r]) {
for (int k=l;k<r;k+=2) {
if (s[k]==s[r]) {
f[l][r]=(f[l][r]+(1ll)*f[l][k]*(f[k+1][r-1]))%mod;
}
}
}
}
}
cout<<f[0][n-1]<<endl;
}
return 0;
}