题意:给定()[]{}的数量分别为l1,l2,l3(0<=l1,l2,l3<=10),规定()不能套在[]和{}的外面,[]不能套在{}的外面,即{} [] ()优先级递减,现在想最大组成嵌套深度为d
(0<=d<=30)的串,问一共有多少种方法。
题解:首先很容易想到记忆化dfs,dp[a][b][c][d]表示{}[]()分别有a b c个形成最大嵌套深度为d的方法数,然后找到子状态,但是麻烦的是d是多个子问题求max得到的,这样
在dfs的过程中没办法控制哪一个是最大的,看了下提示瞬间石化。
dp[a][b][c][d]表示{}[]()分别有a b c个形成最大嵌套深度小于等于d的方法数,这样能得到子状态,由于优先级的问题改变遍历{}[]()的顺序即可,枚举当前在左边套在最
外面的是{}[]或(),最后得到答案为dp[a][b][c][d] – dp[a][b][c][d-1](d == 0时候答案为dp[a][b][c][d])。因为dfs时分开的两个串是独立的,所以不会有重复计数。
PS:网上有一个想法说的很好:把括号的嵌套看成是一棵树就简单些,这棵树的最大深度为 D,()节点下面不能有{}[]节点,[]节点下面不能有{}节点,然后从上往下
依次摆放节点。
Sure原创,转载请注明出处。
#include <iostream> #include <cstdio> #include <memory.h> using namespace std; const int maxn = 12; const int maxm = 32; const int mod = 11380; int dp[maxn][maxn][maxn][maxm]; bool vis[maxn][maxn][maxn][maxm]; int a,b,c,d; int dfs(int l1,int l2,int l3,int dep) { if(l1 == 0 && l2 == 0 && l3 == 0) { vis[l1][l2][l3][dep] = true; return dp[l1][l2][l3][dep] = 1; } if(dep == 0) { vis[l1][l2][l3][dep] = true; return dp[l1][l2][l3][dep] = 0; } if(vis[l1][l2][l3][dep]) { return dp[l1][l2][l3][dep]; } int res = 0; for(int i=0;i<=l3;i++) { if(i) { res += (dfs(0,0,i-1,dep-1) * dfs(l1,l2,l3-i,dep)) % mod; res %= mod; } for(int j=0;j<=l2;j++) { if(j) { res += (dfs(0,j-1,i,dep-1) * dfs(l1,l2-j,l3-i,dep)) % mod; res %= mod; } for(int k=1;k<=l1;k++) { res += (dfs(k-1,j,i,dep-1) * dfs(l1-k,l2-j,l3-i,dep)) % mod; res %= mod; } } } vis[l1][l2][l3][dep] = true; return dp[l1][l2][l3][dep] = res; } int main() { memset(vis,false,sizeof(vis)); while(~scanf("%d %d %d %d",&a,&b,&c,&d)) { dfs(a,b,c,d); if(d) dfs(a,b,c,d-1); int ans; if(d == 0) ans = dp[a][b][c][d]; else ans = ((dp[a][b][c][d] - dp[a][b][c][d-1]) % mod + mod) % mod; printf("%d\n",ans); } return 0; }