题意:有一个字符串 s. 这个字符串是一个彻底匹配的括号序列.在这个彻底匹配的括号序列里,每一个括号都有一个和它匹配的括号
你如今能够给这个匹配的括号序列中的括号染色,且有三个要求:
每一个括号只有三种状况,不上色,上红色,上蓝色.
每对括号必须只能给其中的一个上色,且必须给一个上色
相邻的两个不能上同色,能够都不上色
求知足条件的括号序列染色的方法数c++
假设不染色为0,另外两种色为1,2
那对于一个匹配的括号对来讲,只容许(1,0),(2,0),(0,1),(0,2)
定义\(f[l][r][i][j]\):区间\([l,r]\)的左端点状态为\(i\),右端点状态为\(j\)时的当前区间方案数
能够得出当\(l+1=r\)时,只有上述四种存在方案数1ubuntu
除此之外还有两种状况
1.当前处理区间\([l,r]\)两端点刚好是一个匹配括号对
2.当前处理区间\([l,r]\)两端点不是一个匹配括号对spa
状况1能够先递归处理\([l+1,r-1]\)的方案,最后合并方案数,这里要注意相邻位置不可取同等颜色
状况2也可分治\([l,match[l]]\)(状况1)和\([match[l]+1,r]\)两个区间处理.方案数是两个区间合法方案的乘积,一样要注意约束条件code
具体的操做看代码递归
#include<bits/stdc++.h> #define rep(i,j,k) for(int i=j;i<=k;i++) #define rrep(i,j,k) for(int i=j;i>=k;i--) #define println(a) printf("%lld\n",(ll)(a)) #define printbk(a) printf("%lld ",(ll)(a)) typedef long long ll; using namespace std; const int MAXN = 732+11; const ll oo = 0x3f3f3f3f3f3f3f3f; const ll ooo= 0x3f3f3f3f; const int MOD = 1e9+7; ll read(){ ll x=0,f=1;register 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; } ll dp[MAXN][MAXN][3][3]; int match[MAXN]; char str[MAXN]; void DP(int l,int r){ if(l==r){ rep(i,0,2) rep(j,0,2) dp[l][r][i][j]=0; return; } if(l+1==r){ dp[l][r][0][0]=0; dp[l][r][1][1]=0; dp[l][r][2][2]=0; dp[l][r][1][2]=0; dp[l][r][2][1]=0; dp[l][r][1][0]=1; dp[l][r][0][1]=1; dp[l][r][2][0]=1; dp[l][r][0][2]=1; }else if(match[l]==r){ DP(l+1,r-1); rep(i,0,2) rep(j,0,2) dp[l][r][i][j]=0; rep(i,0,2) rep(j,0,2){ // i==j ok/ err 0 if(j!=1) dp[l][r][0][1]+=dp[l+1][r-1][i][j]; if(j!=2) dp[l][r][0][2]+=dp[l+1][r-1][i][j]; if(i!=1) dp[l][r][1][0]+=dp[l+1][r-1][i][j]; if(i!=2) dp[l][r][2][0]+=dp[l+1][r-1][i][j]; rep(x,0,2) rep(y,0,2){ if(dp[l][r][x][y]>=MOD) dp[l][r][x][y]%=MOD; } } }else{ DP(l,match[l]); DP(match[l]+1,r); rep(i,0,2) rep(j,0,2) dp[l][r][i][j]=0; rep(i,0,2) rep(j,0,2) rep(x,0,2) rep(y,0,2)if((x==y&&x==0)||(x!=y)){ dp[l][r][i][j]+=dp[l][match[l]][i][x]*dp[match[l]+1][r][y][j]%MOD; if(dp[l][r][i][j]>=MOD) dp[l][r][i][j]%=MOD; } } } int main(){ while(~scanf("%s",str+1)){ memset(dp,-1,sizeof dp); stack<int> stk; int n=strlen(str+1); rep(i,1,n){ if(str[i]=='(') stk.push(i); else{ int pos=stk.top();stk.pop(); match[pos]=i; } } DP(1,n); ll res=0; rep(i,0,2) rep(j,0,2) res=(res+dp[1][n][i][j])%MOD; println(res); } return 0; }
题意:给出n个节点的值\(a_i\),若两节点的值gcd不为1则可连边,问这n个点可否构成一个二叉树字符串
//压根没想到是区间DPget
\(L[l][r]\):以\(r\)为根,\([l,r-1]\)可否构成左子树
\(R[l][r]\):以\(l\)为根,\([l+1,r]\)可否构成右子树
\(ok[l][r]\):\([l,r]\)可否构成二叉树it
用区间DP的方法不断扩展l,r可行范围便可
具体画个图很容易看出来左右的扩展方式ast
https://paste.ubuntu.com/p/VdBdSjMYYR/class
题意:一个大小为n*m的巧克力,要分出面积k的巧克力(能够拼凑出来),每次操做只能横着掰开或竖着掰开,代价为掰开的边长的平方,求最小代价
思绪良久,仍是\(O(n^5)\)暴力出全部状况
https://paste.ubuntu.com/p/7QJcxg8SFc/
题意:求出全部\([L,R]\)的回文子串个数
容斥定理,对于\([L,R]\)内的贡献,来自于\([L,R-1]\)和\([L+1,R]\),重复贡献为\([L+1,R-1]\)
若是\(str_L=str_R\)且内部为回文串,那么贡献要+1(SB如我又加了一遍[L+1,R-1])
https://paste.ubuntu.com/p/4S35zpYzRz/
题意:求\(s[a,b]\) && \(s[x,y]\)的回文子串对的个数,\(1≤a≤b<x≤y≤n\)
感受和前面的题目很类似,但答案并不是\(dp[1][i]*dp[i+1][n]\),这样存在重复贡献
所以引入一个\(g[i]\):以\(i\)结尾的回文串个数
这样就能够\(g[i]*dp[i+1][n]\)不重不漏地求出全部数量
https://paste.ubuntu.com/p/5TjgK8kX6R/
题意:给出一个\(a[1...n]\)的环,n接回1,环中每删除一个数的代价是数两旁元素的gcd值,求最小删除代价
\(dp[i][j]\):\([i,j]\)区间只剩\(i\)和\(j\)的最小代价
而后xjb乱写竟然过了?