计蒜客-乘法最大 动态规划

本文介绍了一种使用动态规划解决特定数学问题的方法,即在给定的数字串中插入指定数量的乘号,以求得最大的可能乘积。通过定义状态dp[i][j]表示前i个数中插入j个乘号的最大乘积,采用递推公式进行求解。

题目链接 https://www.jisuanke.com/course/709/36575

题解: 参考 https://blog.youkuaiyun.com/ruzhuxiaogu/article/details/25695671#commentBox

使用动态规划,dp[i][j]表示前i个数中插入j个乘号的乘积最大值,

要求插入j个乘号,可以将j个乘号拆出最后一个单独出来,这样原来的数就分成了2部分,

假如前一部分长度为k,则dp[i][j] = max(dp[k][j-1] * number(k+1,len)), k>=j

相当于把第二部分的长度从1遍历一遍,取最大值,具体见代码或者上述参考题解

#include <cstdio>
#include <algorithm> 
#include <cstring>
using namespace std;
typedef long long ll;
char ch[25] = {0};// 输入的数字字符串
int len;// 数字长度
int K;// 插入的乘号个数 
ll dp[25][15];// dp[i][j] 前i个数字中插入j个乘号的最大乘积 
// 计算数字l到r位的数(数字从1开始计数) 
ll Product(int l,int r) {
	ll res = 0;
	for(int i = l;i <= r;i++) {
		res = res*10 + ch[i] - '0';
	}
	return res;
}

int main() {
	scanf("%d%d",&len,&K);
	scanf("%s",ch+1);
	// 动态规划
	for(int i = 1;i <= len;i++) {
		dp[i][0] = Product(1,i);
	}
	// 遍历插入的乘号数目 
	for(int j = 1;j <= K;j++) 			
		// 遍历前i个数 
		for(int i = 1;i <= len;i++) {
			if(i < j+1) continue;
			ll temp = 0;
			for(int k = j;k < i;k++) {
				temp = max(temp,dp[k][j-1]*Product(k+1,i));
			}
			dp[i][j] = temp;
		}
	 
	printf("%lld\n",dp[len][K]);
	
	return 0;
}

 

在牛网的一道与数字变换相关的算法问题中,题目要求找出将一个整数 $ A $ 转换为另一个整数 $ B $ 所需的最少操作次数,每次操作可以将当前数字乘以一个给定的整数 $ k $,或者将当前数字加1或减1。这类问题常见于编程竞赛和算法训练中,考察了对搜索策略、数学优化以及状态转移的理解。 ### 解题思路 该问题可以通过 **广度优先搜索**(BFS)或 **双向BFS** 来求解,以找到从 $ A $ 到 $ B $ 的最短路径(即最少操作次数)。其核心在于对每一步可能的操作进行扩展,并记录已访问的状态以避免重复算。 若 $ A > B $,显然乘法操作只会让差距更大,因此只能使用减1操作。否则,当 $ A < B $ 时,需要综合考虑以下几种操作: - 若 $ B \mod k = A \mod k $,可以通过若干次减法或加法操作达到目标。 - 否则,考虑将 $ A $ 乘以 $ k $,并继续搜索。 - 对于 $ B $ 较大的情况,可以先通过乘法接近目标,再使用加减法微调。 此外,还可以采用 **逆向思维**,从 $ B $ 倒推回 $ A $,每次操作可以是除以 $ k $(前提是 $ B \mod k = 0 $),或者加1、减1。这种方式在某些情况下能更高效地减少搜索空间。 ### 示例代码(BFS实现) ```cpp #include <iostream> #include <queue> #include <unordered_map> #include <cmath> using namespace std; int minOperations(int A, int B, int k) { if (A == B) return 0; if (A > B) return A - B; // 只能减 unordered_map<int, int> visited; queue<int> q; q.push(B); visited[B] = 0; while (!q.empty()) { int curr = q.front(); q.pop(); int steps = visited[curr]; if (curr == A) return steps; // 减法操作 if (curr - 1 >= A && !visited.count(curr - 1)) { visited[curr - 1] = steps + 1; q.push(curr - 1); } // 加法操作 if (curr + 1 <= B && !visited.count(curr + 1)) { visited[curr + 1] = steps + 1; q.push(curr + 1); } // 除法操作(逆向乘法) if (curr % k == 0 && !visited.count(curr / k)) { visited[curr / k] = steps + 1; q.push(curr / k); } } // 若无法到达A,理论上不会发生 return -1; } ``` ### 算法复杂度分析 - **时间复杂度**:取决于状态空间的扩展情况,最坏情况下为 $ O(B - A) $,但在使用剪枝策略(如限制最大扩展次数、使用双向BFS)后可显著优化。 - **空间复杂度**:由哈希表 `visited` 和队列 `q` 决定,最坏情况下也为 $ O(B - A) $。 ### 注意事项 - 对于 $ A > B $ 的情况,直接返回 $ A - B $。 - 当 $ k = 1 $ 时,乘法操作无效,此时只能使用加减操作。 - 需要处理整数溢出问题,尤其是当 $ B $ 非常大时,应限制最大扩展值。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值