NOI2001——陨石的秘密(计数类dp)
原题链接
思路:
看的是lyd老师的题解
dp状态找的很巧妙:
d
p
[
i
]
[
j
]
[
k
]
[
l
]
dp[i][j][k][l]
dp[i][j][k][l]表示深度不超过
i
i
i,用了
j
j
j对花括号,
k
k
k对中括号,
l
l
l对小括号组成的括号序列的数量。
这样只需要枚举四层:
枚举深度
枚举花括号的个数
枚举中括号的个数
枚举小括号的个数
枚举的顺序也是要注意的,因为
S
S
SS
SS表达式的要求。
接下来考虑转移:
划分依据是将括号序列分为第一部分和第二部分。
由于两个
S
S
SS
SS序列拼接时,深度为
m
a
x
(
d
1
,
d
2
)
max(d1,d2)
max(d1,d2),所以第二部分的长度可以为
[
1
,
i
]
[1,i]
[1,i]中的任意数,对应到
d
d
d
p
p
p数组的第一维为
i
i
i。
由于是求的数量,所以是乘法原理,根据第一部分最外层的不同括号进行再次枚举。
比如当第一部分最外层为花括号时,第一部分里面可以是花括号、中括号、小括号,就要进行三层for枚举。
for(int p=1;p<=j;p++)
for(int q=0;q<=k;q++)
for(int r=0;r<=l;r++)
dp[i][j][k][l]=(dp[i][j][k][l]+dp[i-1][p-1][q][r]*dp[i][j-p][k-q][l-r])%mod;
假设第一部分花费了 p p p个花括号, q q q个中括号, r r r个小括号,留给第二部分的就是 j − p j-p j−p个花括号, k − q k-q k−q个中括号, l − r l-r l−r个小括号。由于第一部分的最外层已经确定是花括号了,所以对应转移的深度范围也变成了 [ 1 , i − 1 ] [1,i-1] [1,i−1],对应dp的第一维为 i − 1 i-1 i−1。
代码:
///#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll>PLL;
typedef pair<int, int>PII;
typedef pair<double, double>PDD;
#define I_int ll
inline ll read()
{
ll x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
#define read read()
#define closeSync ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define multiCase int T;cin>>T;for(int t=1;t<=T;t++)
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define perr(i,a,b) for(int i=(a);i>(b);i--)
ll ksm(ll a, ll b, ll p)
{
ll res = 1;
while(b)
{
if(b & 1)res = res * a % p;
a = a * a % p;
b >>= 1;
}
return res;
}
const int inf = 0x3f3f3f3f;
#define PI acos(-1)
const double eps = 1e-8;
const int maxn = 2e5 + 7,mod=11380;
int dp[31][11][11][11],l1,l2,l3,l4,d;
int main()
{
l1=read,l2=read,l3=read,d=read;
for(int i=0;i<=d;i++) dp[i][0][0][0]=1;
for(int i=1;i<=d;i++)///枚举深度
for(int j=0;j<=l1;j++)
for(int k=0;k<=l2;k++)
for(int l=0;l<=l3;l++){
if(j>0){///第一层最外层是{}
for(int p=1;p<=j;p++)
for(int q=0;q<=k;q++)
for(int r=0;r<=l;r++)
dp[i][j][k][l]=(dp[i][j][k][l]+dp[i-1][p-1][q][r]*dp[i][j-p][k-q][l-r])%mod;
}
if(k>0){///第一层最外面是[]
for(int q=1;q<=k;q++)
for(int r=0;r<=l;r++)
dp[i][j][k][l]=(dp[i][j][k][l]+dp[i-1][0][q-1][r]*dp[i][j][k-q][l-r])%mod;
}
if(l>0){
for(int r=1;r<=l;r++)
dp[i][j][k][l]=(dp[i][j][k][l]+dp[i-1][0][0][r-1]*dp[i][j][k][l-r])%mod;
}
}
int tmp=dp[d][l1][l2][l3];
if(d) tmp-=dp[d-1][l1][l2][l3];
if(tmp<0) tmp=(tmp+mod)%mod;
cout<<tmp;
return 0;
}