练习题 更小的数

题目

问题描述

图片描述

小蓝有一个长度均为 n 且仅由数字字符 0∼9 组成的字符串,下标从 0 到 n−1,你可以将其视作是一个具有 n 位的十进制数字 num,小蓝可以从 num 中选出一段连续的子串并将子串进行反转,最多反转一次。小蓝想要将选出的子串进行反转后再放入原位置处得到的新的数字 numnew​ 满足条件 numnew​<num,请你帮他计算下一共有多少种不同的子串选择方案,只要两个子串在 num 中的位置不完全相同我们就视作是不同的方案。

注意,我们允许前导零的存在,即数字的最高位可以是 00,这是合法的。

输入格式

输入一行包含一个长度为 n 的字符串表示 num(仅包含数字字符 0∼9),从左至右下标依次为 0∼n−1。

输出格式

输出一行包含一个整数表示答案。

样例输入

210102

样例输出

8

样例说明

一共有 88 种不同的方案:

  1. 所选择的子串下标为 0∼10∼1,反转后的 numnew=120102<210102numnew​=120102<210102;
  2. 所选择的子串下标为 0∼20∼2,反转后的 numnew=012102<210102numnew​=012102<210102;
  3. 所选择的子串下标为 0∼30∼3,反转后的 numnew=101202<210102numnew​=101202<210102;
  4. 所选择的子串下标为 0∼40∼4,反转后的 numnew=010122<210102numnew​=010122<210102;
  5. 所选择的子串下标为 0∼50∼5,反转后的 numnew=201012<210102numnew​=201012<210102;
  6. 所选择的子串下标为 1∼21∼2,反转后的 numnew=201102<210102numnew​=201102<210102;
  7. 所选择的子串下标为 1∼41∼4,反转后的 numnew=201012<210102numnew​=201012<210102;
  8. 所选择的子串下标为 3∼43∼4,反转后的 numnew=210012<210102numnew​=210012<210102;

评测用例规模与约定

对于 2020% 的评测用例,1≤n≤100;

对于 4040% 的评测用例,1≤n≤1000;

对于所有评测用例,1≤n≤5000。

运行限制

语言最大运行时间最大运行内存
C++1s256M
C1s256M
Java2s256M
Python33s256M
PyPy33s256M
Go3s256M
JavaScript3s256M
提交代码
//更小的数

//字符串仅由0-9数字组成
//下标从0开始
//反转0次或1次
//反转后的数字变小
//求有多少种方案
//求所有解 
//连续一段元素的群体关系
//动态规划 
//前面的数字越小字符串越小 
//如果一个字符串可以反转,那么它的两端加上相同的数字或相对固定大小的数字也可以
//起始位置、终点位置、反转方法种数 

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

string num;//原数字字符串
int n;//数字字符串长度
int result = 0;//结果
int dp[5000][5000];//dp[i][j]:起始位置为i,终点位置为j,可以有多少种反转的方法 

int main(){
	//输入原数字字符串
	cin >> num;
	
	n = num.length();//数字字符串长度 
	
	//DP
	//斜下一条一条找 
	int i = 0,j = 0;
	int cnt = 0;//记录j返回位置 
	while(true){
		if(i == j){
			dp[i][j] = 0;
		}else{
			dp[i][j] = dp[i + 1][j] + dp[i][j - 1] - dp[i + 1][j - 1];
			int t = 0;
			//判断反转后是否更小
			while((i + t) < (j - t) && num[i + t] == num[j - t]){
				t++;
			}
			if(num[i + t] > num[j - t]){
				dp[i][j]++;
			}
		}
		
		i++;
		j++;
		
		if(i == 1 && j == n){
			//结束
			break; 
		}else if(j == n){
			i = 0;
			j = cnt + 1;
			cnt++;
		}
	}
	
	//输出结果
	result = dp[0][n - 1];
	printf("%d",result); 
	
	return 0;
} 
 总结

解题思路:反转一段连续的子串,属于连续一段元素的群体关系,可以使用滑动指针,但是求的不是最优解而是所有解,所以使用动态规划。所选取的子串的状态由起始字符位置和终止字符位置决定,所以用dp[i][j]表示:起始字符位置为i,终止字符位置为j,这段字符串中共有多少种方案。其中当i==j即起始字符和终止字符位置相同时,方案数为0,除此之外,dp[i][j]=dp[i+1][j]+dp[i][j-1]-dp[i+1][j-1];注意删除其中[i+1,j]和[i,j-1]两段中重复的方案,如果[i,j]整体反转也符合要求,则dp[i][j]在上述式子的基础上加1。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

OAIM.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值