思路
首先我们发现可以搜索,但是明显会TLE,因为组合数学的结果是以指数倍增长的,结果会很大,明显不行。
由于不要输出路径,那么考虑DP。
令
f
i
,
j
,
k
,
d
f_{i,j,k,d}
fi,j,k,d为深度
d
d
d,{}
i
i
i对,[]
j
j
j对,()
k
k
k对的结果。
我们发现这样很难得出结果。
我们令
f
i
,
j
,
k
,
d
f_{i,j,k,d}
fi,j,k,d为深度小于等于
d
d
d,{}
i
i
i对,[]
j
j
j对,()
k
k
k对的结果,貌似可以好一点得到结果。
我们利用{}[]()将字符串进行分割。令A B,那么就有三种情况:{A}B [A]B (A)B,当然A B也可能是空串。不难证明这种DP方式是不重不漏的。
DP式如下:
f
i
,
j
,
k
,
d
=
∑
a
=
0
i
−
1
∑
b
=
0
j
∑
c
=
0
k
f
a
,
b
,
c
,
d
−
1
×
f
i
−
a
−
1
,
j
−
b
,
k
−
c
,
d
+
∑
a
=
0
j
−
1
∑
b
=
0
k
f
i
,
j
−
1
−
a
,
k
−
b
,
d
×
f
0
,
a
,
b
,
d
−
1
+
∑
a
=
0
k
−
1
f
i
,
j
,
k
−
a
−
1
,
d
×
f
0
,
0
,
a
,
d
−
1
f_{i,j,k,d}=\sum^{i-1}_{a=0}\sum^{j}_{b=0}\sum^{k}_{c=0}f_{a,b,c,d-1}\times f_{i-a-1,j-b,k-c,d}\ +\ \sum^{j-1}_{a=0}\sum^{k}_{b=0}f_{i,j-1-a,k-b,d}\times f_{0,a,b,d-1}\ +\ \sum^{k-1}_{a=0}f_{i,j,k-a-1,d}\times f_{0,0,a,d-1}
fi,j,k,d=a=0∑i−1b=0∑jc=0∑kfa,b,c,d−1×fi−a−1,j−b,k−c,d + a=0∑j−1b=0∑kfi,j−1−a,k−b,d×f0,a,b,d−1 + a=0∑k−1fi,j,k−a−1,d×f0,0,a,d−1
初值:
f
0
,
0
,
0
,
0
=
1
f_{0,0,0,0}=1
f0,0,0,0=1(空串深度为
0
0
0只有
1
1
1种可能)
细节
得出最后的 结果是
f
l
1
,
l
2
,
l
3
,
d
−
f
l
1
,
l
2
,
l
3
,
d
−
1
f_{l_1,l_2,l_3,d}-f_{l_1,l_2,l_3,d-1}
fl1,l2,l3,d−fl1,l2,l3,d−1。
但是,如果这么做,其实是错误的,因为
l
1
,
l
2
,
l
3
,
d
l_1,l_2,l_3,d
l1,l2,l3,d有可能会为
0
0
0,那么我们就可以发现以下结论:
| l 1 , l 2 , l 3 l_1,l_2,l_3 l1,l2,l3 | d d d | 结果 | 解释 |
|---|---|---|---|
| 都为 0 0 0 | 0 0 0 | 1 1 1 | 空串深度为 0 0 0只有 1 1 1种可能 |
| 都为 0 0 0 | 不为 0 0 0 | 0 0 0 | 空串不可能深度为 1 1 1 |
| 不都为 0 0 0 | 0 0 0 | 0 0 0 | 非空串的深度一定大于等于 1 1 1 |
最后注意精度问题,要用unsigned long long。
代码
#include<cstdio>
#include<iostream>
#define maxn 12
#define MOD (ll)11380
#define MODx f[i][j][k][d]%=MOD;
using namespace std;
typedef unsigned long long ll;
ll f[maxn][maxn][maxn][39];
int l1,l2,l3,D;
int main(){
scanf("%d%d%d%d",&l1,&l2,&l3,&D);
for(int i=0;i<=D;i++)
f[0][0][0][i]=1;
for(int d=1;d<=D;d++)
for(int i=0;i<=l1;i++)
for(int j=0;j<=l2;j++)
for(int k=0;k<=l3;k++){
if(i==0&&j==0&&k==0) continue;
for(int a=0;a<i;a++)
for(int b=0;b<=j;b++)
for(int c=0;c<=k;c++){
f[i][j][k][d]=(f[i][j][k][d]+(ll)f[i-a-1][j-b][k-c][d]*f[a][b][c][d-1])%MOD;
}
for(int a=0;a<j;a++)
for(int b=0;b<=k;b++){
f[i][j][k][d]=(f[i][j][k][d]+(ll)f[i][j-a-1][k-b][d]*f[0][a][b][d-1])%MOD;
}
for(int a=0;a<k;a++){
f[i][j][k][d]=(f[i][j][k][d]+(ll)f[i][j][k-a-1][d]*f[0][0][a][d-1])%MOD;
}
}
if(D==0){
if(l1==0&&l2==0&&l3==0) printf("1");
else printf("0");
}
else if(l1==0&&l2==0&&l3==0){
printf("0");
}
else cout<<(f[l1][l2][l3][D]-f[l1][l2][l3][D-1]+MOD)%MOD;
return 0;
}
该博客详细解析了NOI 2001竞赛中关于‘陨石的秘密’的问题。由于直接搜索会导致超时,作者提出使用动态规划(DP)来解决。通过设定状态转移方程,并处理边界条件,如字符串分割后的三种情况,最终得出解决方案。需要注意的是,计算结果的精度问题,应使用unsigned long long类型确保正确性。


被折叠的 条评论
为什么被折叠?



