CF 1272F Two Bracket Sequences (括号dp)
题目地址
Solution
首先题目中有两个括号串 \(s\) 和 \(t\) ,考虑先设计两维表示 \(s\) 匹配到的位置和 \(t\) 匹配到的位置。
接着根据 括号dp的一般套路:设计一维表示当前栈中的左括号数量 (ygt大佬喜欢形象地把其称为 “前缀和”),所以状态就出来了:
\[f[i,j,k] \texttt{表示 s 匹配到 i , t 匹配到 j , 栈中有 k 个左括号,的最小新序列长度}\]
转移挺简单的,新序列要么增加一个 '(' , 要么增加一个 ')' , 直接转移即可。
但是转移的阶段顺序很难写,但是 根据递推不好写我们就递归的原则 我们可以写一个 记忆化搜索。具体实现和路径输出请看代码。
Code
Talk is cheap.Show me the code.
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
inline int read() {
int 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<<3)+(x<<1)+(ch^48); ch=getchar(); }
return x * f;
}
const int N = 207;
int n,m;
int dp[N][N][N<<1];
char s[N],t[N],op[N][N][N<<1];
struct Node {
int x,y,z;
}pr[N][N][N<<1];
int Solve(int i,int j,int k) {
if(dp[i][j][k] != -1) return dp[i][j][k];
if(i==n+1 && j==m+1) return k; //栈中还有k个左括号,我们要k个右括号与之匹配
int res1 = INF, res2 = INF, ni1 = ((i<=n&&s[i]=='(')?i+1:i), nj1 = ((j<=m&&t[j]=='(')?j+1:j), ni2 = ((i<=n&&s[i]==')')?i+1:i), nj2 = ((j<=m&&t[j]==')')?j+1:j);
if(k <= 200) res1 = Solve(ni1,nj1,k+1) + 1;
if(k > 0) res2 = Solve(ni2,nj2,k-1) + 1;
if(res1 < res2) {
op[i][j][k] = '('; dp[i][j][k] = res1; pr[i][j][k] = (Node)<%ni1,nj1,k+1%>;
} else {
op[i][j][k] = ')'; dp[i][j][k] = res2; pr[i][j][k] = (Node)<%ni2,nj2,k-1%>;
}
return dp[i][j][k];
}
void Print(int i,int j,int k) {
Node tmp = pr[i][j][k];
if(op[i][j][k]=='(' || op[i][j][k]==')') cout<<op[i][j][k];
if(tmp.x!=-1) Print(tmp.x, tmp.y, tmp.z);
else {
for(int l=1;l<=k;++l) cout<<')'; //输出这k个右括号
}
}
int main()
{
//freopen("My.out","w",stdout);
scanf("%s%s",s+1,t+1);
n = strlen(s+1), m = strlen(t+1);
memset(dp, -1, sizeof(dp));
memset(pr, -1, sizeof(pr));
Solve(1,1,0);
Print(1,1,0);
return 0;
}
Summary
重点在了解括号dp的一般套路。