洛谷P5694 [NOI2001] 陨石的秘密
作者:互联网
看完题面的第一反应是暴力
直接四维dp [i,j,k,l]表示深度为i,大中小括号分别为j,k,l时的方案
但发现s=ab这种情况不好处理,还要再枚举一个比d小的深度d'
时间复杂度变成了 D²*L的六次方
想开一个辅助数组g [i,j,k,l]表示深度小于i时的方案数,就可以直接拿出来用了
时间复杂度的问题解决了,但还不知道怎么处理重复计算
比如 [ ]()[ ]
在枚举[ ]时,()[ ]会被计算到
在枚举[ ]()时,[ ]会被计算到
难道是限制括号总长度来限制重复?胡思乱想完,无奈地看了题解
欸,真有趣!
解决重复计算是强制给a最外面匹配括号,就不会出现上述情况了,可以多画几个理解一下
(写到这里,强制转移,强制链接(处理环),最近遇到不止一次了,真是神奇的思想)
时间复杂度是,直接将我设想的辅助数组设为状态
直接四维dp [i,j,k,l]表示深度小于等于i,大中小括号分别为j,k,l时的方案
最后统计答案时用差分的思想dp[d][l1][l2][l3]-dp[d-1][l1][l2][l3]
(似乎做树形dp时有在洛谷遇到,也是计数,大意是给定节点,求二叉树深度为h时的方案数,那题也是直接设求不出来)
嗷,都是很巧妙的思想>_<
一些写代码过程中的细节:
1注意大中小括号有区别,不能乱,匹配小括号时要求大中括号为0
2算答案时可能出负数,要加一个mod
3不再是dp [0,0,0,0]=1了,是dp[i,0,0,0]=1,i属于(0,d)
这里是因为后面在枚举过程中,会出现和空串搭配的情况(也就是题面描述的第二种深度计算方式,我们的方程把三种方式都结合在一起了)
比如:
dp[i][j][k][l]=(dp[i][j][k][l]+(dp[i-1][x-1][y][z]*dp[i][j-x][k-y][l-z])%11380)%11380;
会出现x-1,y,z都为0的情况,如果只有dp[0,0,0,0]为1的话就wa了
get:边界的设置跟状态有关,要斟酌一下
4
if(j) { for(int x=1;x<=j;x++) for(int y=0;y<=k;y++) for(int z=0;z<=l;z++) { dp[i][j][k][l]=(dp[i][j][k][l]+(dp[i-1][x-1][y][z]*dp[i][j-x][k-y][l-z])%11380)%11380; } }
要保证j首先得不为0,才能强制加,第一次因为这个挂了。
code:
#include<bits/stdc++.h> using namespace std; int l1,l2,l3,d,dp[31][11][11][11]; int main() { freopen("lys.in","r",stdin); cin>>l1>>l2>>l3>>d; memset(dp,0,sizeof(dp)); 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) { for(int x=1;x<=j;x++) for(int y=0;y<=k;y++) for(int z=0;z<=l;z++) { dp[i][j][k][l]=(dp[i][j][k][l]+(dp[i-1][x-1][y][z]*dp[i][j-x][k-y][l-z])%11380)%11380; } } if(k) { for(int y=1;y<=k;y++) for(int z=0;z<=l;z++) { dp[i][j][k][l]=(dp[i][j][k][l]+(dp[i-1][0][y-1][z]*dp[i][j][k-y][l-z])%11380)%11380; } } if(l) { for(int z=1;z<=l;z++) { dp[i][j][k][l]=(dp[i][j][k][l]+(dp[i-1][0][0][z-1]*dp[i][j][k][l-z])%11380)%11380; } } } cout<<(dp[d][l1][l2][l3]-dp[d-1][l1][l2][l3]+11380)%11380; }
标签:洛谷,P5694,int,括号,l2,l3,NOI2001,l1,dp 来源: https://www.cnblogs.com/liyishui2003/p/16182157.html