金字塔 题解

Posted 。✧* ꧁王者꧂✧*

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了金字塔 题解相关的知识,希望对你有一定的参考价值。

哒哒哒~

初看本题时,一头雾水,根本不知从何处下手,只隐隐感到,如果将两个相同的字符视为一颗子树的根,应该会有多种可能吧。当然,这种想法也只是一瞬即逝,但却是解锁本题的关键。
于是,就根据这个思路来DP,那么答案应该就是 d p [ 1 ] [ n ] dp[1][n] dp[1][n]了,这是显然的。那么,如何DP呢?可以发现,本题算是道区间DP,且只有当 d p [ l ] [ r ] dp[l][r] dp[l][r] l l l r r r处的字符相同时,才会有值。所以,从小区间转向大区间时,小区间的两端字符一定是相同的,这是转移的条件。那么,我们怎么样才能不重不漏地并且不多余地计算出 f [ l ] [ r ] f[l][r] f[l][r]的值呢?这是本题十分关键的一点。
所以,为了能够完美地计算 f [ l ] [ r ] f[l][r] f[l][r],我们每次转移时,可以枚举当前根连接的第一颗子树的大小(因为是深度优先遍历,第一颗子树大小不同,可以证明这两棵子树在深度优先遍历的意义下一定不同,即使二者形态相同),而找寻最大的子树,便是枚举 l l l r r r中间的一个 k k k,使 l + 1 l+1 l+1 k − 1 k-1 k1处的字符相同且 k k k r r r的字符相同。于是,枚举就好。
总复杂度: O ( n 3 ) O(n^3) O(n3)
本人的写法是 d f s dfs dfs,但依旧是这个思路(参考《算法进阶》)。

#include<bits/stdc++.h>
using namespace std;
#define int long long
char s[350];
int M=1e9;
long long f[310][310];//f[i][j]指遍历从i到j后的最大方案数; 
int work(int a,int b)
{
	if(a>b) return 0;
	if(s[a]!=s[b]) return 0;
	if(a==b) return 1;
	if(f[a][b]!=-1) return f[a][b];
	f[a][b]=0;
	for(int k=a+2;k<=b;k++)
	{
		f[a][b]=(f[a][b]+(long long)(work(a+1,k-1)*work(k,b))%M)%M;
	}
	return f[a][b]%M;
}
signed main()
{
	freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);
	scanf("%s",s+1);
	int len=strlen(s+1);
	memset(f,-1,sizeof(f));
	cout<<work(1,len);
	return 0;
}

以上是关于金字塔 题解的主要内容,如果未能解决你的问题,请参考以下文章

Acwing P284 金字塔 题解

线性DP相关题目题解

Successor HDU - 4366 分块

Java 求解划分字母区间

求镂空的倒金字塔java代码....金字塔可用*代替! 谢了

PHP代码生成金字塔