P1249 最大乘积 ——————洛谷

题目

P1249 最大乘积
在这里插入图片描述
分析

一道高精+贪心

一个数拆的越多乘积会越大,但不要拆出1,这样浪费了
所以就2, 3, 4, 5, ~
但也不能连续的一直拆, 因为可能不够拆了
所以就可以跳出那个数 把那个数拆给前面的
大概就这样
上代码

#include<bits/stdc++.h>
using namespace std;
const int N = 500;
string f(int x) {
  char c[10], temp;
  int i = 0, j;
  do {
    c[i] = x%10+'0';
    x /= 10;
    i++;
  } while(x != 0);
  c[i] = '\0';
  for (i--, j = 0; j <= i/2; j++, i--) {
    temp = c[j];
    c[j] = c[i];
    c[i] = temp;
  }
  return c;
}
string mul(string a, string b) {
  string s;
  int A[N], B[N], C[N];
  int la = a.size(), lb = b.size();
  memset(A, 0, sizeof(A));
  memset(B, 0, sizeof(B));
  memset(C, 0, sizeof(C));
  for (int i = 0; i < la; i++) A[la-i] = a[i]-'0';
  for (int i = 0; i < lb; i++) B[lb-i] = b[i]-'0';
  for (int i = 1; i <= la; i++) {
    for (int j = 1; j <= lb; j++) {
      C[i+j-1] += A[i]*B[j];
      C[i+j] += C[i+j-1]/10;
      C[i+j-1] %= 10; 
    }
  }
  if (C[la+lb]) s += C[la+lb]+'0';
  for (int i = la+lb-1; i >= 1; i--) {
    s += C[i]+'0';
  } 
  return s;
}
int n, c = 1, ans[1001];
string s[1001], m = "1";
int main() {
  scanf("%d", &n);
  if ( n <= 4 ){
    printf ( "%d\n%d\n", n, n );
    return 0;
  }//特判,如果n小于5,自己本身就是最优解
  for (int i = 2; i <= n; i++) {
    if (n >= i) {
      ans[c++] = i;
      s[c-1] = f(i);
      n -= i;
    } else break;
  }
  for (int i = c-1; i >= 1; i++) {
    if (n > 0) {
      ans[i]++;
      s[i] = f(ans[i]);
      n--;
    }
  }
  if (n > 0) {
    ans[c-1]++;
    s[c-1] = f(ans[c-1]);
  }
  for (int i = 1; i < c; i++) {
    cout << ans[i] << " ";
    m = mul(s[i], m);
  }
  cout << endl << m;
  return 0;
}

chapter 章节

### 关于洛谷 P1249 最大乘积问题的 C++ 解题思路 对于给定的一个正整数 `n` 和分割次数 `k`,目标是将 `n` 分割成 `k` 个部分使得这些部分的乘积最大化。这个问题可以通过动态规划来解决。 #### 动态规划的状态定义 设 `dp[i][j]` 表示前 `i` 位数字分成 `j` 段所能得到的最大乘积[^3]。 #### 初始化 - 对于只有一段的情况,即 `dp[i][1]` 就是从第1位到第i位组成的整个数值。 #### 状态转移方程 为了求解 `dp[i][j]` 的值,可以考虑最后一个切割位置 `p` (其中 `1 ≤ p < i`),则状态转移方程为: \[ dp[i][j]=\max(dp[p][j-1]*num(p+1,i)) \] 这里 `num(p+1,i)` 是指从第 `p+1` 到第 `i` 位所表示的子串对应的十进制数值。 #### 边界条件 当 `j=1` 或者 `i=j` 时,显然不需要进一步划分,因此可以直接赋初值;其他情况下通过上述公式计算得出结果。 下面是具体的代码实现: ```cpp #include<iostream> #include<string> using namespace std; const int MAX_N = 50; string s; long long f[MAX_N][MAX_N], num[MAX_N][MAX_N]; // 计算字符串s中从l到r之间的数字转换成long long型 void calc_num() { for (int l = 0; l < s.size(); ++l) for (int r = l; r < s.size(); ++r) { if (!l && !r) num[l][r] = s[l]-'0'; else num[l][r] = num[l][r-1]*10+s[r]-'0'; } } int main(){ int N, K; cin >> N >> K >> s; // 预处理每一段的数值 calc_num(); // 初始化边界情况 for(int i = 0; i<s.length();++i){ f[i+1][1]=num[0][i]; f[i+1][i+1]=f[i][i]*10+(s[i]-'0'); } // 填表过程 for(int j = 2;j<=K;++j)//枚举段数 for(int i = j;i<N;++i)//枚举终点 for(int k = j-1;k<i;++k)// 枚举上一次切分的位置 f[i][j]=max(f[i][j],f[k][j-1]*num[k][i]); cout << f[N-1][K]<<endl; } ``` 这段代码实现了基于动态规划的方法来寻找最优解,并且能够有效地处理高精度运算的需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值