区间动态规划

整理一下关于区间动态规划的题,《括号配对》和《密码脱落》

括号配对

括号的配对,即可以包含着配对(()),也可并列配对()().
F[i][j]状态表示

集合:所有将[i,j]变成合法括号的序列的集合。
属性:最小值

i和j为遍历时候区间的两端。

状态计算
先分为包含配对和并列配对两类。

1.包含配对f[i+1][j-1] (i,j即左右都被用上)
f[i][j-1]+1 ( j需要添加,即i被用上)
f[i+1][j]+1(i需要被添加,即j被用上)
这里的i被用上其实没有一个确定的状态表示,但f[i][j-1]包含了这个状态,并且求的是最小值,所以没有影响。

2.并列配对 f[i][k]+f[k+1][j]
即在i和j中找是否存在已经配对好的[i,k]和[k+1,j],[i,k]是配对好的没有一个状态能表示,但是可以用包含它的f[i][k]来表示。
即f[i][k]可能是(()()())这样的,而[i,k]是表示左边第一个()。

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

//集合:所有将[i,j]变成合法括号序列的方案的集合
//属性:最小值

//第一大类:相当于括号回文
//第二大类:并列的组
const int N = 110, INF = 100000000;

int n;
int f[N][N];

bool is_match(char l, char r)
{
    if (l == '(' && r == ')') return true;
    if (l == '[' && r == ']') return true;
    return false;
}

int main()
{
    string s;
    cin >> s;
    n = s.size();

    for (int len = 1; len <= n; len ++ )
        for (int i = 0; i + len - 1 < n; i ++ )
        {
            int j = i + len - 1;
            //也可以设置成1
            //因为i和j相同时,代表只有一个括号,那么就需要一个配对
            f[i][j] = INF;
            //左边
            //左右匹配   则不需要配对
            if (is_match(s[i], s[j]))
                f[i][j] = f[i + 1][j - 1];
            //左右不匹配
            //有存在没配对的,所以有个加1
            //这里看的是  不算在里面的那个端点
            //如果有不算在里面,那就加一个,即配对一下
            //结果找的是  要配对的  括号个数的  最小值
            if (j >= 1)
                f[i][j] = min(f[i][j], min(f[i][j - 1], f[i + 1][j]) + 1);
            //右边第二大类
            //有个k+1,所以k不能=j
            for (int k = i; k < j; k ++ )
                f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j]);
        }
    //左端点   到   右端点
    cout << f[0][n - 1] << endl;
    return 0;
}

密码脱落

#include <cstdio>
#include <string.h>
//加上一个字母  等价于  将其删去
//所求的即为  总长度 -  最大回文串
//这个  回文串可以不连续

//即问题变成了  如何寻找 最大回文子序列

//集合:l 到 r 之间的回文子序列的集合
//属性:长度的最大值


//写了一个l或者r肯定是包含之前状态的
//至于之前那个状态是啥不确定
//所以才写了这么多情况讨论

const int N = 1010;

char s[N];
int f[N][N];

int main()
{
    scanf("%s", s);
    int n = strlen(s);
    //先循环区间长度1~n
    //在循环左端点,最后把右端点算出来
    //右端点:左端点+长度-1    记住!!!!!!!
    //从0开始,右端点<n
    //如果从1开始,右端点<=n
    for (int len = 1; len <= n; len ++ )
        for (int l = 0; l + len - 1 < n; l ++ )
        {
            int r = l + len - 1;
            if (len == 1)
                f[l][r] = 1;
            else
            {
                //l和r不在里面
                //即问题转变成了求 f[l + 1][r - 1] 的最大值
                //再把l和r加上
                if (s[l] == s[r])
                    f[l][r] = f[l + 1][r - 1] + 2;
                //r没有找到配对
                //那么右边的集合里必须有一个和 l相等
                //状态里面没有说   哪个一定与l相等
                // [l,r-1] 包含上面的集合,即可能有与l匹配的,也可能没有
                //如果这种选择比f[l][r]大,即有与l匹配的
                //取的是最大,所以可以这么比



                //因为l是从小到大枚举
                //所以当前状态的前面状态肯定是已经计算过的


                if (f[l][r - 1] > f[l][r])
                    f[l][r] = f[l][r - 1];

                if (f[l + 1][r] > f[l][r])
                    f[l][r] = f[l + 1][r];

                //上面两种情况包含了f[l+1,r-1]
            }
        }

    printf("%d\n", n - f[0][n - 1]);

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值