括号类问题总结 持续更新
前言
本来说今天要写写状压dp的,结果大致看了几道题,发现剩下的都不会,然后突发奇想去看了看去年的CSP-S的题,由于那会儿我只学了不到三个月吧,所以啥都不会借口,就打了40多分,惨。今天重新做了一下,D1T1,这么简单的题为什么我当时不会!其实就是一个分治,写了不到十分钟就A了。主要是想说一下T2,T3仍然不会逃,T2应该算是一个很经典的问题了,括号配对,只不过这个题是挪到了树上,用一个有辅助的树形dp就能解决,这个括号类的问题值得总结一下。
裸的配对
括号配对
感觉最基础的配对只要好好学过奥赛应该都会,这道题算是难一点的了。
括号配对时用要用到一个栈,为什么是栈呢?因为栈的特点是后进先出,如果遇到一个括号,那么它肯定和之前遇到的最近的同类型括号匹配,所以用个栈,这道题的难点是它要考虑括号的大小问题,为了解决这个问题,可以根据题意确定一下每个括号的优先级,然后先比较优先级,再进行配对。
左括号与右括号的优先级顺序应该是正好相反的,注意一下就行。
还有建议写一个函数来判断能否配对,这样比较方便return。
#include<cstdio>
#include<cstring>
using namespace std;
const int N=300;
char stk[N],str[N];
int top;
int ch(char x){
if(x=='<')return 1;
if(x=='(')return 2;
if(x=='[')return 3;
if(x=='{')return 4;
if(x=='}')return 5;
if(x==']')return 6;
if(x==')')return 7;
if(x=='>')return 8;
}
void judge(){
top=0;
int l=strlen(str+1);
for(int i=1;i<=l;i++){
if(ch(str[i])<=4){
if(!top||ch(str[i])<=ch(stk[top]))
stk[++top]=str[i];
else {
printf("NO\n");
return ;
}
}else {
if(!top||ch(str[i])+ch(stk[top])!=9){
printf("NO\n");
return;
}
top--;
}
}
if(top)printf("NO\n");
else printf("YES\n");
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%s",str+1);
judge();
}
}
稍作变形
然后其实D1T2的括号树就是由最基本的原型拓展来的。
先考虑链的情况,因为链最基本并且也有一部分部分分。如果设\(f_u\)表示从根节点到\(u\)的合法括号序列总数,\(p_u\)表示\(u\)的父亲,那么显然有转移方程\(f_u=f_{p_u}+以u为右端点的合法括号序列数\),\(f_{p_u}\)在dfs的时候就可以求出来,关键是后边那个怎么求,我们可以利用拆分的思想,把一条链拆成两部分,开一个栈存可以被用来配对的左括号的节点编号,假设在\(v\)遇到一个右括号,如果栈为空,不需要任何操作,因为什么配对都做不了,如果栈不空呢?
如果它不空的话,取出栈顶元素\(u\),以u为右端点的合法括号序列数就是以\(p_u\)为右端点的合法括号序列数+1,加的一就是括号序列\(uv\),那么之前的还用考虑吗?不用,因为它已经被包含在这种情况里边了。
链的问题解决了,树上边就只需要一个回溯,因为每层dfs最多只会改变一个元素,所以最后再还原一下现场就行。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=5e5+10;
struct Edge{
int to,nxt;
}e[N];
int h[N],idx,p[N],top,stk[N];
char str[N];
ll f[N],g[N];
void Ins(int a,int b){
e[idx].to=b;e[idx].nxt=h[a];h[a]=idx++;
}
void dfs(int u){
if(str[u]=='('){
stk[++top]=u;
f[u]=f[p[u]];
for(int i=h[u];~i;i=e[i].nxt)dfs(e[i].to);
top--;
}else {
if(!top){
f[u]=f[p[u]];
for(int i=h[u];~i;i=e[i].nxt)
dfs(e[i].to);
}else {
int now=stk[top--];
g[u]=g[p[now]]+1;
f[u]=f[p[u]]+g[u];
for(int i=h[u];~i;i=e[i].nxt)
dfs(e[i].to);
stk[++top]=now;
}
}
}
int main(){
int n;
memset(h,-1,sizeof(h));
scanf("%d%s",&n,str+1);
for(int i=2;i<=n;i++){
scanf("%d",&p[i]);
Ins(p[i],i);
}
dfs(1);
ll ans=0;
for(int i=1;i<=n;i++)
ans^=i*f[i];
printf("%lld\n",ans);
}
从这个代码开始图论我要换码风了,Head用h代替,len用idx,别问为什么,问就是跟别的dalao学的。