蓝桥杯练习系统-乘积最大(暴力法||DP)

算法训练 乘积最大
时间限制:1.0s 内存限制:256.0MB

锦囊1
动态规划。
问题描述

今年是国际数学联盟确定的“2000——世界数学年”,又恰逢我国著名数学家华罗庚先生诞辰90周年。在华罗庚先生的家乡江苏金坛,组织了一场别开生面的数学智力竞赛的活动,你的一个好朋友XZ也有幸得以参加。活动中,主持人给所有参加活动的选手出了这样一道题目:

设有一个长度为N的数字串,要求选手使用K个乘号将它分成K+1个部分,找出一种分法,使得这K+1个部分的乘积能够为最大。

同时,为了帮助选手能够正确理解题意,主持人还举了如下的一个例子:

有一个数字串:312, 当N=3,K=1时会有以下两种分法:

312=36
  31
2=62

这时,符合题目要求的结果是:31*2=62

现在,请你帮助你的好朋友XZ设计一个程序,求得正确的答案。

输入格式

程序的输入共有两行:
  第一行共有2个自然数N,K(6≤N≤40,1≤K≤6)
  第二行是一个长度为N的数字串。

输出格式

输出所求得的最大乘积(一个自然数)。

样例输入

4 2
  1231
样例输出
62

思路1:
dp[N] [K]字符串前N位插入K个乘号后所能得到的最大值
例如312:当N=3, K=1时,从N=1, K=0开始推导
dp[1] [0] = 3
dp[2] [0] = 31
dp[3] [1] = max(0 , dp[1,0] * 12) = max(0,36) = 36
dp[3] [1] = max(36 , dp[2,0] * 2) = 62

例如1231:当N = 4,K = 2时,从N = 1 ,K = 0开始推导
dp[1] [0]:1
dp[2] [0]:12
dp[3] [0]:123
dp[4] [0]:1231
dp[2] [1]:max(0 , dp[1] [0] * 2) = 2;
dp[3] [1]:max(2 , dp[1] [0] * 23) = 23;
dp[3] [1]:max(23,dp[2] [0] * 3) = 36;
dp[3] [2]:max(0,dp[1] [1] * 23) = 0;
dp[3] [2]:max(0,dp[2] [1] * 3) = 6;
dp[4] [1]:max(0,dp[1][0] * 231) = 231;
dp[4] [1]:max(231,dp[2] [0] * 31) = 372;
dp[4] [1]:max(372,dp[3] [0] * 1) = 372;
dp[4] [2]:max(0,dp[1] [1] * 231) = 0;
dp[4] [2]:max(0,dp[2] [1] * 31) = 62;
dp[4] [2]:max(62,dp[3] [1] * 1) = 62;

动态规划方程: dp[i][j] = max(dp[i][j],dp[k][j-1] * get(k + 1,i));

#include<iostream>
using namespace std;
int n,k;
int a[100];
int dp[100][100];
int get(int x,int y)//用来取出某个符号后面的数字,取的是下标x到y之间的数字组成的数值
{
    int sum = 0;
    for(int i = x;i <= y;i ++)
        sum = sum * 10 + a[i];
    return sum;
}
void ok()
{
    for(int i = 1;i <= n;i ++)
        dp[i][0] = get(1,i);
    
    for(int i = 2;i <= n;i ++)
        for(int j = 1;j <= k && j < i ;j ++)
            for(int k = 1;k < i ;k ++)
                dp[i][j] = max(dp[i][j],dp[k][j-1] * get(k + 1,i));
                //在前i个数中,有j个乘号
}
int main()
{
    cin >> n >> k;
    string s;
    cin >> s;
    for(int i = 0;i < s.size();i ++)
        a[i + 1] = s[i] - '0';
    
    ok();
    cout << dp[n][k];
    return 0;
}

思路2:利用暴力求解法:
例如312 N = 3, K = 1
分别列出
3,12
31,2
首先从第一个数开始,得到3 * 12 = 36;
然后一次类推,得到31 * 2 = 62;
每次推K个符号即可

例如1231 N = 4, K = 3
然后判断哪个是K个符号可以分开的,如果是的话,就累乘在一起,找出最大值即可
分别列出
1,2,23,231
12,3,31
123,1
1231

则可以分别得出
1,2,31;
1,23,1;
1,231 (不符合进入求解最大值的循环当中)
12,3,1;
123,1 (不符合进入求解最大值的循环当中)
1231 (不符合进入求解最大值的循环当中)

#include <iostream>
#include <algorithm>
using namespace std;
string a;
int n,k,maxs;
void bfs(int t,int s,int l)//t是乘号个数,s是用来乘的数
{
    int xs = 0;
    if(t == k)
    {
        for(int i = l;i < n;i++)//在l和n之间的数进行循环求最大值
            xs = xs * 10 + a[i] - '0';
        if(s * xs > maxs)//将传进来的s跟后面的值相乘
            maxs = s * xs;
        return ;
    }
    
    for(int i = l;i < n;i++)//l-n,依次得到乘法的成员
    {
        xs = xs * 10 + a[i] - '0';
        bfs(t + 1,s * xs,i + 1);
    }
}
int main()
{
    cin >> n >> k;
    cin >> a;
    maxs = 0;
    bfs(0,1,0);
    cout << maxs << endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Αиcíеиτеǎг

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

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

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

打赏作者

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

抵扣说明:

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

余额充值