切割01串问题(dp动态规划问题)

题目概述:

给定一个长度为 𝑛 的 01 串,定义如下操作为一次 “切割”:
将长度大于 1 的字符串分割为两个非空的连续字串,记分割出来的左侧字串 a 中 0 的出现次数为
C 0 C_0 C0,右侧字串 b 中 1 出现的次数为 C 1 C_1 C1​ ,需要满足 L ≤ L \leq L | C 0 − C 1 C_0 - C_1 C0C1| ≤ R \leq R R
你每次切割完,都会得到两个新 01 串,你可以继续选择这些已经被你切出来的 01 串做切割,只要满足切割条件。
jackle 想问你最多可以切割多少次?

输入描述:

第一行输入3个整数,n( 1 ≤ 1 \leq 1 n ≤ 500 \leq 500 500 L , R ( 0 ≤ L,R (0\leq L,R(0 L ≤ R L \leq R LR ≤ 500 ) \leq 500) 500),分别表示字符串长度,和题目中的两个参数。
第二行输入1个长度为n的01串。

输出描述:

输出最多能切割多少次

事例

输入

6 2 3
011011

输出

3

说明
其中一种切割次数最多的切法如下:
第一次切割可以切:0 ∣ 11011,然后选择 11011 这个串继续做切割。
第二次切割可以切:1 ∣ 1011,然后选择 1011 这个串继续做切割。
第三次切割可以切:1 ∣ 011。

分析题目: 看到题目中的最多,就应该想到需要用动态规划来解决(当时做的时候懵逼了,没有想出来)。题目就是让我们找有多少个字串满足切割条件,也就是求字串的数量只不过加上了一个限定条件,这样大概框架就有了。

DP
状态表示:dp[i][j],表示左端点为i,右端点为j的所有满足条件的字串的集合
状态计算:dp[i][j]=max(dp[i][j], dp[i][k] + dp[k][j] + 1) (k 为切割点)

代码表示:

#include <iostream>
#include <algorithm>
using namespace std;

const int N = 505;
// 分别表示长度为N的字符串中0的数量和1的数量
int sum0[N], sum1[N], dp[N][N];
int n, l, r;
string s;
int main(){
    cin >> n >> l >> r;
    cin >> s;
    
    // 求前缀和
    // 这里因为我们的前缀和需要从1开始,但字符串是从0开始所以sum0 sum1是i+1才对
    for(int i = 0; i < s.length(); i ++){
        sum0[i + 1] = sum0[i] + (s[i]=='0');
        sum1[i + 1] = sum1[i] + (s[i]=='1');
    }

	// 求字串的模板
    // 字串长度不能取到n,否则就不是字串了
    for(int len = 1; len < n; len ++){
        // i为字串的起始位置
        for(int i = 1; i + len <= n; i ++){
            // 切割点的位置不能到字串长度的最后一位,否则右字串将是空串
            for(int j = i; j < i + len; j ++){
                int c0 = sum0[j] - sum0[i - 1];
                int c1 = sum1[i + len] - sum1[j];
                
                int sub = abs(c0 - c1);
                if(sub >= l && sub <= r){
                    dp[i][i + len] = max(dp[i][i + len], dp[i][j] + dp[j + 1][i + len] + 1);
                }
            }
        }
    }
    
    cout << dp[1][n];
    return 0;
}

此题不算难题,但做的时候卡了挺久,就是因为没看出题的本质。
有不懂的地方可以评论区留言,欢迎点赞关注加收藏!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值