切割 01 串 2.0(区间DP)

目录

题目

题目描述

输入描述

输出描述

输入

输出

样例说明

解题思路(区间DP)

完整代码(C++)


题目

题目描述

jackle 在校赛的时候出过一道 "切割 01 串" 的题目,如今他又出了一道切割 01 串的题目:
给定一个长度为 n 的 01 串,定义如下操作为一次 "切割":

  • 长度大于 1的字符串分割为两个非空的连续字串,记分割出来的左侧字串 a 中 0 的出现次数为 C0,右侧字串 b 中 1 出现的次数为 C1​,需要满足 L≤∣C0​−C1​∣≤R 。

你每次切割完,都会得到两个新 01串,你可以继续选择这些已经被你切出来的 01 串做切割,只要满足切割条件。

jackle 想问你最多可以切割多少次?

输入描述

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

输出描述

输出最多能切割多少次?

输入

6 2 3
011011

输出

3

样例说明

其中一种切割次数最多的切法如下:

第一次切割可以切:0 ∣ 11011,然后选择 11011 这个串继续做切割。

第二次切割可以切:1 ∣ 1011,然后选择 1011这个串继续做切割。

第三次切割可以切:1 ∣ 011。

解题思路(区间DP)

设 dp[i][j] 表示从 i 到 j 这个串中最多可以切割的次数,我们要求解的答案就是 dp[1][n] 。

对于 i 到 j 这个子串,他的最大切割次数可以由他切割后的两个子串得到,假设在 i 到 j 这个子串,我们从k(i\leq k< j)处切割,如果在此处切割是一种可行的方案(L\leqslant ∣C0​−C1​∣\leqslant R )

可得转移方程:
                                            dp[i][j]=dp[i][k]+dp[k+1][j]

显而易见,我们要枚举  k  的位置来确定最优的切割位置,求得最优解。

枚举子串,第一层枚举字串长度,然后枚举左端点 i ,进而确定右端点 j=len+i-1 。

枚举左端点 i 到右端点 j中可分割的位置  k  。利用动态转移方程求解。

完整代码(C++)

#include<bits/stdc++.h> 
using namespace std;
#define int long long
const int N=510;
int cnt0[N],cnt1[N];//cnt0[i]、cnt1[i]:前i个字符中0、1的个数 
int dp[N][N];//dp[i][j]表示从i到j这个串中最多可以切割的次数 


void solve(){
	
	int n,l,r;
	cin>>n>>l>>r;
	string s;cin>>s;
	s=" "+s;
	
	//前缀和处理一下0和1的个数 
	for(int i=1;i<=n;i++){
		cnt0[i]=cnt0[i-1]+(s[i]=='0'?1:0);
		cnt1[i]=cnt1[i-1]+(s[i]=='1'?1:0);
	}
	
	//枚举字串长度 
	for(int len=1;len<=n;len++){	
		
		//枚举左端点 
		for(int i=1;i<n;i++){	
			
			//确定右端点 
			int j=i+len-1;
			if(j>n)break;	
				
			//枚举切割位置 
			for(int k=i;k<j;k++){
				int C0 = cnt0[k] - cnt0[i - 1]; //i到k中0的个数 
                int C1 = cnt1[j] - cnt1[k]; //k+1到j中1的个数 
                if (abs(C0 - C1) >= l && abs(C0 - C1) <= r)
                    //符合切割条件,利用动态转移方程求解 
					dp[i][j] = max(dp[i][j], dp[i][k] + dp[k+1][j] + 1);
			}
		} 
	}
	
	//1到n区间中最多可以切割的次数 
	cout<<dp[1][n]<<endl;
}


signed main(){
	std::ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	
	int t=1;
	//cin>>t;
	while(t--){
		solve();
	} 
	
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值