算法设计与分析 SCAU11090 最大m段乘积和最小m段和(优先做)

news2024/12/23 6:23:06

11090 最大m段乘积和最小m段和(优先做)

时间限制:1000MS 代码长度限制:10KB
提交次数:0 通过次数:0

在这里插入图片描述

题型: 编程题 语言: G++;GCC;VC;JAVA

Description

一个n位十进制整数S,若将S划分为m个段,则可以得到m个整数。
(1)这m个整数的乘积称为S的一个“m段乘积”,对于给定的S和m,求S的最大m段乘积。
(2)这m个整数的和称为S的一个“m段和”,对于给定的S和m,求S的最小m段和。


输入格式

输入:三个整数,n,m,S。
第一个n表示S的位数,第二个m表示分割的段数,第三个数为需要被分段的n位十进制数S。
n、m和S三个数中间空格相连,这里1<=m<=n,n<=10,即S、S的最大m段乘积、S的最小m段和这三个
数都用int型即可,虽然输出的数可能很大,但这里32位整数够了,测试数据没有超过32位整数,即
无需考虑多位的高精度数。

例如,十进制数3456的“最大3段乘积”为1020。因为3456划分3个段有如下情形:
3456=672,3456=810,3456=1020。
3456的“最小3段和”为45,因为3+4+56=63,3+45+6=54,34+5+6=45。


输出格式

输出:
第一行计算出的最大m段乘积和最小m段和,中间空格相连。
第二行写出最大m段乘积的乘法表达式。
第三行写出最小m段和的加法表达式。

这里约定:
(1)若有多种分段方法使得最大m段乘积相等且都最大,则优先输出靠左的段更短小的这种方式。
比如输入3 2 111,这里最大m段乘积的乘法表达式为:111=11,而不要输出111=11
(2)若分出的某段数字有0开头的,不输出0。
比如输入5 3 20001,这里输出:201=0,(其实代表着:20001,这里001,只写1即可)
(3)若只分一个段,表达式也就写1个段的数等于某个数。
比如输入2 1 12,输出:12=12
(4)最小m段和的加法表达式也同样满足前面的约定(1)(2)(3)。


输入样例

4 3 3456


输出样例

1020 45
3456=1020
34+5+6=45


解题思路

1. dp 方程定义

由于 f 和 t 分析过程一样,所以下面只对 f 进行分析(即最大 m 段乘积和)

  • f[i][j]:前 i 个数划分为 j 段时的最大乘积,f[n][m] 为所求
  • fs[i][j]:前 i 个数划分为 j 段时各个最大乘积的分割位置
  • fa[i]:存储最大 m 段乘积的每个值(组成部分)


2. 状态转移方程

设 w(h,t) 是 S 从 h 位开始的共 t 位数字组成的十进制数,约定从1开始从左向右对位进行编号,即最左一位定为第1位。

对于 f(i, j),这里只考虑 i>=j 的情况,因为每个段至少1位,因此 i 必然大于等于 j ,因为比如3位数字(i),你最多只能分为3、2、1段(j),即 i >= j。

边界
  1. 当 j = 1 时,f(i, 1) = w(1, i), 1 <= i <= n 表示当只划分1个段时,最大段乘积
    就是从第 1 位开始共 i 位数(i>=1)的十进制数值。

  2. 当 j>=2 时,计算 f(i, j) 的动态规划的递归式如下:f(i,j) = max{ f(k, j-1) * w(k+1, i-k) | k from j-1 to i-1 } j >= 2 && j <= i <= n
    (即让 k 从 j - 1 到 i - 1 变化,找 f(k, j-1) 和 w(k + 1, i - k) 乘积的最大值)

这个公式这样理解:
当 j >= 2 && j <= i <= n 时,现在要求解的问题是前i位划分为 j 个段的最大 j 段乘积,
这时考虑前 k 位,划分 j - 1 个段(因为最后一个段至少占1位,而前 j - 1 个段又至少有 j - 1 位,所以 j-1 <= k <= i-1),先获得这 j - 1 个段的最大段乘积(从前 k 个数中选),再乘以从第 k + 1 位到第 i 位(共 i - k 位,因为前 i 个数,从 k 开始)的十进制数。
即让 k 从 j - 1 到 i - 1 循环,求 f(k, j-1) 和 w(k+1, i-k) 乘积的最大值。

	for (int j = 2; j <= m; j++) {
		for (int i = j; i <= n; i++) {
			f[i][j] = f[j - 1][j - 1] * w(j, i - j + 1);
			fs[i][j] = j;
			t[i][j] = t[j - 1][j - 1] + w(j, i - j + 1);
			ts[i][j] = j;
			for (int k = j; k <= i - 1; k++) {
				int tMax = f[k][j - 1] * w(k + 1, i - k);
				if (f[i][j] < tMax) {
					f[i][j] = tMax;
					fs[i][j] = k + 1;
				}
				int tMin = t[k][j - 1] + w(k + 1, i - k);
				if (t[i][j] > tMin) {
					t[i][j] = tMin;
					ts[i][j] = k + 1;
				}
			}
		}
	}

对于求如何获得“最大m段乘积的乘法表达式”

例如 3456 分割成 34 * 5 * 6 = 1020

  • 通过求 f 时不断记录索引位置到 fs 数组,然后通过遍历 fs 来不断获得各个数字段并记录到 fa,即 fa[j] = w(fs[i][j], k - fs[i][j]);
  • 其中 fs[i][j] 为前 i 个数划分为 j 段时求出的索引位置;
  • k 为这一段数值结束位置,因此对于 k,从后往前遍历,每次循环都将 k 赋值为上一次的断点(即 k = fs[i][j]);
  • 同时 i 要更新成上一次断点的前一位数,毕竟 i 的含义是前 i 个数(即 i = fs[i][j] - 1 ),由于断点处开始的后面部分已经记录完了,那就往断点处前面继续开始找即可,比如 3456,断点为数字5,则后面的第三位和第四位已经被记录过了,下一次的前 i 个数字从数字4开始。

之后输出 f[a] 中的数字即可

	int i = n;
	int k = n + 1;
	for (int j = m; j >= 1; j--) {
		fa[j] = w(fs[i][j], k - fs[i][j]);
		k = fs[i][j]; //记录上次的断点
		i = fs[i][j] - 1; // 由于断点处开始的后面部分已经记录完了,那就往断点处前面继续开始找即可,比如 3456,断点为数字5,则后面的第三位和第四位已经被记录过了,下一次的前 i 个数字从数字4开始
	}
	for (int i = 1; i <= m; i++) {
		cout << fa[i];
		if (i != m)
			cout << "*";
	}

	cout << "=" << f[n][m] << endl;


最小m段和的动态规划递归式

最小m段和公式的分析和最大m段乘积的公式分析是相同的。

边界: 当j=1时,t(i,1) = w(1,i), 1<=i<=n 表示当只划分1个段时,最小段和就是从第1位开始共i位
数(i>=1)的十进制数值。

当j>=2时,计算t(i,j)的动态规划的递归式如下:
t(i,j) = min{ t(k, j-1) + w(k+1, i-k) | k from j-1 to i-1 } j>=2 && j<=i<=n
(即让k从j-1到i-1变化,找t(k, j-1)和w(k+1, i-k)之和的最小值)



更多注释可查看下方的完整代码中,有助于理解

代码如下

#include <iostream>

using namespace std;

int f[11][11]; //最大 m 段乘积,f[n][m] 为所求
int fs[11][11]; //存储最大 m 段乘积分割位置
int fa[11]; //存储最大 m 段乘积的每个值

int t[11][11]; //最小 m 段和,t[n][m] 为所求
int ts[11][11]; //存储最小 m 段和分割位置
int ta[11]; //存储最小 m 段和的每个值

int num[11]; // 存储字符串各个值

int w(int h, int t) {
	int result = 0;
	for (int i = 0; i < t; i++) {
		result = result * 10 + num[h + i];
	}
	return result;
}

int main() { //最大m段乘积,最小m段和
    int n, m, s;
	cin >> n >> m >> s;
	int ss = s;
	for (int i = n; i >= 1; i--) {
		num[i] = ss % 10;
		ss = ss / 10;
	}

	for (int i = 1; i <= n; i++) {
		f[i][1] = w(1, i);
		ts[i][1] = 1;
		t[i][1] = w(1, i);
		fs[i][1] = 1;
	}

	for (int j = 2; j <= m; j++) {
		for (int i = j; i <= n; i++) {
			f[i][j] = f[j - 1][j - 1] * w(j, i - j + 1);
			fs[i][j] = j;
			t[i][j] = t[j - 1][j - 1] + w(j, i - j + 1);
			ts[i][j] = j;
			for (int k = j; k <= i - 1; k++) {
				int tMax = f[k][j - 1] * w(k + 1, i - k);
				if (f[i][j] < tMax) {
					f[i][j] = tMax;
					fs[i][j] = k + 1;
				}
				int tMin = t[k][j - 1] + w(k + 1, i - k);
				if (t[i][j] > tMin) {
					t[i][j] = tMin;
					ts[i][j] = k + 1;
				}
			}
		}
	}
	cout << f[n][m] << " " << t[n][m] << endl;


	int i = n;
	int k = n + 1;
	for (int j = m; j >= 1; j--) {
		fa[j] = w(fs[i][j], k - fs[i][j]);
		k = fs[i][j]; //记录上次的断点
		i = fs[i][j] - 1; // 由于断点处开始的后面部分已经记录完了,那就往断点处前面继续开始找即可,比如 3456,断点为数字5,则后面的第三位和第四位已经被记录过了,下一次的前 i 个数字从数字4开始
	}
	for (int i = 1; i <= m; i++) {
		cout << fa[i];
		if (i != m)
			cout << "*";
	}

	cout << "=" << f[n][m] << endl;



	i = n;
	k = n + 1;
	for (int j = m; j >= 1; j--) {
		//cout << "i "<< i << " j " << j << " " << ts[i][j] << endl;
		ta[j] = w(ts[i][j], k - ts[i][j]);
		k = ts[i][j]; //记录上次的断点
		i = ts[i][j] - 1; //新的i值要减1
	}
	for (int i = 1; i <= m; i++) {
		cout << ta[i];
		if (i != m)
			cout << "+";
	}
	cout << "=" << t[n][m] << endl;


	return 0;
}


最后

对我感兴趣的小伙伴可查看以下链接

  • 我的掘金主页:https://juejin.cn/user/1302297507801358
  • 博客主页:http://blog.zhangjiancong.top/
  • 公众号:Smooth前端成长记录

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/32379.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

有限元在游乐设施中的应用-焊缝计算

作者 | 九峰知己千杯少 一、前言 游乐设施金属结构所采用的连接方式有焊接连接、铆钉连接、普通螺栓连接和高强螺栓连接4种&#xff0c;将两块分离的金属其接头部分局部加热到熔化或半熔化状态&#xff0c;采取施加压力或不加压&#xff0c;或填充其他金属&#xff0c;利用原…

C#上位机系列(4)—示波器一新窗口的建立

本文是讲解C#.net平台的Winform框架下的第四个内容&#xff0c;手把手介绍上位机项目的创建方式以及一些写软件时常用的功能&#xff0c;讲解从零开始的每一个步骤。 本次介绍上位机中新窗口的建立方式和软件示波器的代码原理。 从此节开始&#xff0c;所有代码附后 1.新窗口…

element-plus中menu的基本知识点

在vue后台管理系统中&#xff0c;menu是经常会用到的必不可少的导航组件&#xff0c;这个组件如果是单纯的去使用&#xff0c;很简单。但是在实际开发过程中&#xff0c;与其有关的路由相结合使用&#xff0c;还是容易搞混一些东邪&#xff0c;所以想在这里记录一下。 从产品的…

SuperMap 云原生常见问题解决办法-consul启动异常

在iManager for K8S产品中&#xff0c;如果创建了云套件站点&#xff0c;会有三个consul的服务&#xff0c;consul在云套件中充当的角色是服务发现&#xff0c;服务注册&#xff0c;以及配置共享。如果consul服务失效&#xff0c;云套件的整体服务将不能正常运行。客户在使用云…

用DIV+CSS技术设计的环保主题网站(web前端网页制作课作业)

&#x1f380; 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

教你如何在优麒麟上搭建 RISC-V 交叉编译环境

由于 RISC-V 设备价格昂贵、不易采购等诸多原因&#xff0c;许多小伙伴虽然很感兴趣&#xff0c;但仍无法参与 RISC-V 开发工作&#xff0c;今天就教大家如何在优麒麟上搭建 RISC-V 交叉编译环境&#xff0c;快学起来吧&#xff01; 交叉编译&#xff08;Cross Compile&#x…

收藏 | 机器学习公共数据集集锦(附下载链接)

>>>深度学习Tricks&#xff0c;第一时间送达<<< &#x1f680;&#x1f680;&#x1f680;近期&#xff0c;小海带在空闲之余&#xff0c;收集整理了一批机器学习公共数据集供大家参考。 整理不易&#xff0c;小伙伴们记得一键三连喔&#xff01;&#xff0…

cpu天梯图2022年11月 cpu排行榜天梯图2022

一、i9-13900K 1、13900K参数&#xff1a;24核32线程&#xff0c;睿频5.8GHz&#xff0c;基础功耗125W&#xff0c;最大睿频功耗253W。 2、推荐搭配主板&#xff1a;Z790、B760、Z690、B660。 3、目前单核性能最强的一款CPU&#xff0c;拥有超强的超频能力&#xff0c;为玩家带…

swift指针内存管理-引用

引用探究 首先看一个例子 那么这个 0x0000000000000003 是什么意思呢 回到swift源码 找到关键核心类型 HeapObject 就是 swift 分配内存获取到的结构类型 HeapObject 第一个8字节为 metadata, 接下来是宏 InlineRefCounts 其实 就是泛型真正类型 InlineRefCountBits 至此&am…

Android性能优化方法论

作为一名开发&#xff0c;性能优化是永远绕不过去的话题&#xff0c;在日常的开发中&#xff0c;我们可肯定都会接触过。Android 的性能优化其实是非常成熟的了&#xff0c;成熟的套路&#xff0c;成熟的方法论&#xff0c;成熟的开源框架等等。 对于接触性能优化经验较少的开…

大学生简单个人静态HTML网页设计作品 DIV布局个人介绍网页模板代码 DW学生个人网站制作成品下载

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

APS计划排程结果的量化评价

APS计划排程系统是供应链管理软件中的一种提升管理决策协助系统软件&#xff0c;它通过约束理论、运用多种多样数学分析优化计算方法、有常驻运行内存运转的互动式计算机软件。它借助于某些繁杂的数学运算方式 来处理多种多样自变量&#xff0c;使供应链管理的提升变成事实。 A…

计硕3班-陈陇刚-2022222278-第二章 递归与分治策略 作业

目录 1. 概括第二章学习内容&#xff0c;总结第二章学习心得 1.1 递归 1.1.1 递归的定义 1.1.2 递归的使用场景 1.2 分治 1.2.1 分治的定义 1.2.2 分治法使用场景 1.3 学习心得 2. 描述Fibonacci数列递归算法&#xff0c;并进行时间复杂度分析 2.1 Fibonacci数列定义 …

测试人经验谈:需求不明确也能写出测试用例

测试人员的用例编写&#xff0c;一般基于经验和需求文档进行&#xff0c;但是很多时候项目是没有需求的&#xff0c;特别是领导的某个概念提出&#xff0c;然后开发会根据自己的理解完成&#xff0c;测试人员在没有获得任何依据和需求的情况下如何编写测试用例呢&#xff1f; …

【数据结构】七种排序方法,一篇文章掌握

文章目录前言1. 直接插入排序1.1 画图演示1.2 直接插入排序详细步骤1.3 时间复杂度&#xff0c;空间复杂度分析2. 希尔排序2.1 具体步骤描述2.2 代码详解2.3时间复杂度,空间复杂度分析3. 选择排序3.1 画图讲解3.2 代码讲解3.3 时间复杂度,空间复杂度分析4. 快速排序4.1 画图演示…

【C++初阶7-string】真方便,真舒服

前言 本期浅学一下STL的stirng。 内容概览&#xff1a; STLstring 是什么为什么怎么用&#xff08;接口介绍及使用&#xff09; 博主水平有限&#xff0c;不足之处望请斧正&#xff01; 先导 STL C中非常重要的一个东西&#xff0c;STL(Standard Template Library) 标准…

详解华夏银行iDo平台一体化运维的落地过程

随着数字化转型的深入&#xff0c;基于中台和PaaS架构的一体化运维建设也在各行各业快速展开&#xff0c;但是如何将运维平台本身的能力与企业已有的工具能力进行中台化整合、工具场景如何联动&#xff0c;是个复杂而庞大的工程。 本次&#xff0c;史春志老师以华夏银行运维平…

【Revit二次开发】元素(Element)

图元与图元类型元素元素 元素(Element)也称图元 图元作为revit建模的基础&#xff0c;数量庞大&#xff0c;关系千丝万缕。先了解图元的分类&#xff0c;将会帮助我们整理思路&#xff0c;找到功能开发的关键点。 每一个人都可以按照自己的思路将图元进行分类。建模人员可以按…

vue详细教程

原文链接&#xff1a;https://www.cnblogs.com/MrFlySand/p/16921017.html 02vue的安装 程序说明 1、在body中有2个counter&#xff0c;一个是id&#xff0c;一个是class。 2、创建应用&#xff0c;分别用id和class将配置对象传入 语法&#xff1a;Vue.createApp(方法名).mount…

DPDK之PMD原理

PMD是Poll Mode Driver的缩写&#xff0c;即基于用户态的轮询机制的驱动。本文将介绍PMD的基本原理。 在不考虑vfio的情况下&#xff0c;PMD的结构图如下&#xff1a; 图1. PMD结构图 虽然PMD是在用户态实现设备驱动&#xff0c;但还是依赖于内核提供的策略。其中uio模块&…