【区间dp】 poj 2955 Brackets

首先补充点关于区间dp的知识

 区间DP是一类在区间上进行动态规划的最优问题,一般是根据问题设出一个表示状态的dp,可以是二维的也可以是三维的,一般情况下为二维。然后将问题划分成两个子问题,也就是一段区间分成左右两个区间,然后将左右两个区间合并到整个区间,或者说局部最优解合并为全局最优解,然后得解。区间dp专辑

题意:给定一个字符串,问最长匹配的括号长度是多少?

dp[i][j]代表第i个括号到第j个括号的最大匹配长度

整体套路和最优矩阵链乘一样

我觉得本题分区间的一个很关键的特性在于if a and b are regular, ab is regular  需要找到一个分割点,使得左侧和右侧都达到最优状态

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=110;
int dp[N][N];
int main()
{
    string s;
    while(cin>>s)
    {
        if(s[0]=='e')
            break;
        memset(dp,0,sizeof(dp));
        int len=s.size();
        for(int l=2;l<=len;++l)
        {
            for(int i=0;i+l-1<len;++i)
            {
                int j=i+l-1;
                if((s[i]=='('&&s[j]==')')||(s[i]=='['&&s[j]==']'))
                    dp[i][j]=dp[i+1][j-1]+2;
                for(int k=i;k<=j-1;++k)                         //这个地方写成k=i+1 wa了两发
                    dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]);
            }
        }
        cout<<dp[0][len-1]<<endl;
    }
}

poj 1141 同UVA1626

题意:给定一个由括号构成的字符串,输出由它构成的最小标准括号串。

本题陷阱:输入串可能是空串,因此不能用scanf("%s")的方式输入 (注意!poj上并没有这个坑)

这里dp[i][j]表示第i个括号到第j个括号的最大匹配数(意味最小增加括号数)

参考博客

其中一种输出方式是用pos[i][j]数组记录从第i个括号到第j个括号从哪里分开,使得左部分和右部分需要添加的括号都最少。

输出的时候就分三种情况,落单的括号(i==j) 补全它

dp[i][j]==dp[i+1][j-1] 时pos=-1,不需要补全括号,先输左括号,输出中间部分,最后输右括号

pos!=-1 分别输出(i,pos),(pos+1,j)

如果i>j要return,不然会停不下来

不是很明白为什么在更新dp[i][j]的时候=号情况不更新会T,是因为迭代也左右区间分的尽量均匀会更好吗?

 

 

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N=110;
int dp[N][N];
int pos[N][N];
char s[N];
void solve(int i,int j)
{
    if(i>j) return ;                //没有这一行会runtime error
    if(i==j)
    {
        if(s[i]=='('||s[i]==')') cout<<"()";
        else cout<<"[]";
        return ;
    }
    if(pos[i][j]==-1)
    {
        cout<<s[i];
        solve(i+1,j-1);
        cout<<s[j];
    }
    else
    {
        solve(i,pos[i][j]);
        solve(pos[i][j]+1,j);
    }

}
int main()
{

   // getline(cin,s);
   gets(s);
    //cout<<s<<endl;
    memset(dp,0,sizeof(dp));
        int len=strlen(s);
    for(int l=2;l<=len;++l)
        {
            for(int i=0;i+l-1<len;++i)
            {
                int j=i+l-1;
                if((s[i]=='('&&s[j]==')')||(s[i]=='['&&s[j]==']'))
                {
                    dp[i][j]=dp[i+1][j-1]+2;
                    pos[i][j]=-1;
                }

                for(int k=i;k<=j-1;++k)   //这个地方写成k=i+1 wa了两发
                {
                    int tmp=dp[i][k]+dp[k+1][j];
                    if(tmp>=dp[i][j])      //如果不写=会T???
                    {
                        dp[i][j]=dp[i][k]+dp[k+1][j];
                        pos[i][j]=k;
                    }
                }
            }
        }


//    for(int i=0;i<len;++i)
//    {
//        for(int j=0;j<len;++j)
//            cout<<pos[i][j];
//        cout<<endl;
//    }


    solve(0,len-1);
    cout<<endl;
    return 0;
}

 

另外一种输出的方式是在打印的时候重新检查一下哪种决策最好

其对称版本(dp[i][j]表示从第i个括号到第j个括号最少需要增加多少个括号)

//A
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N=110;
const int INF=0x3f3f3f3f;
int dp[N][N];
int pos[N][N];
char s[N];
void solve(int i,int j)
{
    if(i>j) return ;                //没有这一行会runtime error
    if(i==j)
    {
        if(s[i]=='('||s[i]==')') cout<<"()";
        else cout<<"[]";
        return ;
    }
    if(pos[i][j]==-1)
    {
        cout<<s[i];
        solve(i+1,j-1);
        cout<<s[j];
    }
    else
    {
        solve(i,pos[i][j]);
        solve(pos[i][j]+1,j);
    }

}
int main()
{

   // getline(cin,s);
   gets(s);
    //cout<<s<<endl;
    memset(dp,0,sizeof(dp));

        int len=strlen(s);

    memset(dp,0,sizeof(dp));        //需要添加的括号初始化为0
    for(int i=0;i<len;++i)
        dp[i][i]=1;
    for(int l=2;l<=len;++l)
    {
        for(int i=0;i+l-1<len;++i)        //啊啊啊写成i+len-1了
        {
            int j=i+l-1;
            dp[i][j]=INF;                   //一开始就初始化为INF是错的
            if((s[i]=='('&&s[j]==')')||(s[i]=='['&&s[j]==']'))
            {
                dp[i][j]=dp[i+1][j-1];  //从(i+1,j-1)扩展到(i,j)不需要额外添加括号
                pos[i][j]=-1;
            }
            for(int k=i;k<j;++k)
            {
                int tmp=dp[i][k]+dp[k+1][j];
                if(dp[i][j]>tmp)
                {
                    dp[i][j]=tmp;
                    pos[i][j]=k;
                }
            }
        }
    }

//    for(int i=0;i<len;++i)
//    {
//        for(int j=0;j<len;++j)
//            cout<<pos[i][j];
//        cout<<endl;
//    }


    solve(0,len-1);
    cout<<endl;
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值