Codeforces Round #605 (Div. 3) F. Two Bracket Sequences 三维dp

题目链接:http://codeforces.com/contest/1272/problem/F

题意:给两个括号序列 s1,s2,要求构造一个最短的规范的括号序列 ans,且满足 s1,s2为 ans 的子序列。

 

设有三维dp[i][j][k],表示s1串取到i,第二个字符串取到j,k=左括号数-右括号数,dp[i][j][k]为最小长度

k相当于一个栈的思想,加入一个s1或s2出现的左括号时,k++,加入一个s1或s2出现过的右括号时,(k非0)匹配掉了一个左括号k--,加入左括号右括号相当于入栈出栈

 

当dp[i][j][k]:

k想变成k+1时,必须在s1[i+1]或s2[j+1]出现过一个'('

如果出现在s1[i+1],则dp[i+1][j][k+1]=min(dp[i+1][j][k+1],dp[i][j][k]+1)

如果出现在s2[j+1],则dp[i][j+1][k+1]=min(dp[i][j+1][k+1],dp[i][j][k]+1)

如果同时出现,则相当于加入的左括号为s1、s2共有,dp[i+1][j+1][k+1]=min(dp[i+1][j+1][k+1],dp[i][j][k]+1)

需要用个last数组(pre)标记路径

 当k想变成k-1(加右括号时)同理

 

在做pre处理时可以不用开结构体

pre[tmpi][tmpj][tmpk]=i*1e6+j*1e3+k;

也可以开个char s[maxn][maxn][maxn],来记录s[i][j][k]对应的是左括号还是右括号

附上代码:

#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
const int maxn=200+10;
char s1[maxn],s2[maxn];
int dp[maxn][maxn][maxn*2],n,m,tol;
struct node
{
    int x,y,z;
}pre[maxn][maxn][maxn*2];
int main()
{
    scanf("%s%s",s1+1,s2+1);
    n=strlen(s1+1);
    m=strlen(s2+1);
    tol=0;
    for(int i=1;i<=n;i++)tol+=s1[i]=='(';
    for(int i=1;i<=m;i++)tol+=s2[i]=='(';
    tol=max(tol,n+m-tol);//max(左括号数,右括号数) 
    memset(dp,inf,sizeof dp);
    dp[0][0][0]=0;
    /* 
    dp[i][j][k],s1串取到i,第二个字符串取到j,k=左括号数-右括号数,dp[i][j][k]为最小步数 
    s1的前i位是目前序列的子序列
    s2的前j位是目前序列的子序列
    答案为dp[n][m][0] 
    */ 
    for(int i=0;i<=n;i++)
    {
        for(int j=0;j<=m;j++)
        {
            for(int k=0;k<=tol;k++)
            {
                //放入一个'(',如果s1[i+1]是左括号,则放入的是s1[i+1]的左括号
                //如果s2[j+1]和s1[i+1]都是左括号,则表明新构成的序列里的这个括号既是s1[i+1],又是s2[j+1] 
                int tmpi=i,tmpj=j,tmpk=k+1;
                if(dp[i][j][k]==inf)continue;
                if(s1[i+1]=='(')tmpi++;
                if(s2[j+1]=='(')tmpj++;
                if(dp[tmpi][tmpj][tmpk]>dp[i][j][k]+1)//当s1[i+1]和s2[j+1]都没有左括号时,此语句不会为true 
                {
                    dp[tmpi][tmpj][tmpk]=dp[i][j][k]+1;
                    pre[tmpi][tmpj][tmpk]={i,j,k};
                }
                if(!k)continue;//保证k足够 
                tmpi=i,tmpj=j,tmpk=k-1;
                //放入一个')',因为k为左括号数-又括号数,所以加入右括号后匹配上了一个左括号,k+1-2=k-1 
                if(s1[i+1]==')')tmpi++;
                if(s2[j+1]==')')tmpj++;
                if(dp[tmpi][tmpj][tmpk]>dp[i][j][k]+1)
                {
                    dp[tmpi][tmpj][tmpk]=dp[i][j][k]+1;
                    pre[tmpi][tmpj][tmpk]={i,j,k};
                }
            }
        }
    }

    int id=0;
    for(int i=1;i<=tol;i++)
    {
        if(dp[n][m][i]+i<dp[n][m][id]+id)id=i;//从dp[n][m][0]到dp[n][m][i]要加i个左括号,即为i步 
    }
    vector<char> vec;
    node now={n,m,id};
    while(now.x||now.y||now.z)//最后一步变成000 
    {
        vec.push_back(now.z>pre[now.x][now.y][now.z].z?'(':')');//用z来判断,如果是右括号,z是会变小的
        now=pre[now.x][now.y][now.z];
    }
    int size=vec.size();
    for(int i=size-1;i>=0;i--)printf("%c",vec[i]);
    for(int i=1;i<=id;i++)printf(")"); 
    return 0;
}
posted @ 2019-12-20 18:51  myrtle  阅读(241)  评论(0编辑  收藏  举报