题目传送门https://www.luogu.com.cn/problem/P1018
60 pts
思路
考虑 动态规划——线性dp。
状态
表示 在数字串 的前 项插入第 个乘号所得到的最大乘积。
状态转移方程
对于 ,我们可以枚举第 个乘号的位置 从第 位到第 位,计算把第 个乘号插入在 的位置时的乘积,再将所有算出的乘积取最大值赋值给 。
计算 第 个乘号插入在 的位置的乘积时,可以分成两部分,第 位到第 位 和第 位到第 位,由第 位到第 位插入 个乘号的最大乘积再去乘上第 位到第 位的数字。由于前面 位插入 个乘号时的最大乘积已经算出,被我们储存在 里面,于是就可以得到如下状态转移方程:
表示 数字串 从第 位到第 位的数字。
初始值
如果一个乘号也不插入,那得到的肯定是原数值。
目标值
代码演示
#include <iostream>
#include <cstring>
using namespace std;
#define MAXN 45
#define MAXK 10
int n, k;
string t;
char s[MAXN];
long long dp[MAXN][MAXK];
// dp[i][j]:在 s 的前 i 项插入第 j 个乘号所得到的最大值
long long data(int l, int r) { // 将 s[l, r] 装换为数字
long long num = 0;
for (int i = l; i <= r; i++)
num = num * 10 + s[i] - '0';
return num;
}
int main() {
cin >> n >> k >> t;
for (int i = 0; i < n; i++) s[i + 1] = t[i];
for (int i = 1; i <= n; i++) // 初始化
dp[i][0] = data(1, i);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= min(k, i - 1); j++)
for (int p = j; p < i; p++)
dp[i][j] = max(dp[i][j], dp[p][j - 1] * data(p + 1, i));
cout << dp[n][k];
return 0;
}
提交记录
100 pts
思路
看看数据范围 ,这不是明摆着让我们使用高精吗。
加上高精,果然 A 了。
代码演示
#include <iostream>
#include <cstring>
using namespace std;
#define MAXN 45
#define MAXK 10
int n, k;
string t;
char s[MAXN];
string dp[MAXN][MAXK];
// dp[i][j]:在 s 的前 i 项插入第 j 个乘号所得到的最大值
string data(int l, int r) { // 提取 s[l, r]
string num = "";
for (int i = l; i <= r; i++)
num += s[i];
return num;
}
string mul(string a, string b) { // 高精乘
if (a == "0" || b == "0") return "0";
int lx = a.length(), ly = b.length(), lz = lx + ly;
int x[lx + 2], y[ly + 2], z[lz + 2];
memset(z, 0, sizeof z);
string ans = "";
for (int i = 0, j = lx; i < lx; i++, j--)
x[j] = a[i] - '0';
for (int i = 0, j = ly; i < ly; i++, j--)
y[j] = b[i] - '0';
for (int i = 1; i <= lx; i++) // 不进位的乘法
for (int j = 1; j <= ly; j++)
z[i + j - 1] += x[i] * y[j];
// 进位
int w = 0;
for (int i = 1; i <= lz; i++) {
z[i] += w;
w = z[i] / 10;
z[i] %= 10;
}
// 处理剩余进位
while (w) {
z[++lz] += w;
w = z[lz] / 10;
z[lz] %= 10;
}
// 去除前缀 0
while (z[lz] == 0)
lz--;
// 存放答案
for (int i = lz; i >= 1; i--)
ans += z[i] + '0';
return ans;
}
string _max(string a, string b) {
int la = a.length(), lb = b.length();
if (la < lb) return b;
else if (la > lb) return a;
// 逐位比较
for (int i = 0; i < la; i++) {
if (a[i] < b[i]) return b;
else if (a[i] > b[i]) return a;
}
return a;
}
int main() {
cin >> n >> k >> t;
for (int i = 0; i < n; i++) s[i + 1] = t[i];
for (int i = 1; i <= n; i++) // 初始化
dp[i][0] = data(1, i);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= min(k, i - 1); j++)
for (int p = j; p < i; p++) {
string temp = mul(dp[p][j - 1], data(p + 1, i));
dp[i][j] = _max(dp[i][j], temp);
}
cout << dp[n][k];
return 0;
}
提交记录
100 pts
思路
直接暴力 dfs + 高精度。
俗话说:
暴力出奇迹,打表拿省一。
代码演示
#include <iostream>
#include <cstring>
using namespace std;
#define MAXN 45
#define MAXK 10
int n, k;
string t, ans = "0";
char s[MAXN];
string data(int l, int r) { // 提取 s[l, r]
if (l > r) return "1";
string num = "";
for (int i = l; i <= r; i++)
num += s[i];
return num;
}
string mul(string a, string b) { // 高精乘
if (a == "0" || b == "0") return "0";
int lx = a.length(), ly = b.length(), lz = lx + ly;
int x[lx + 2], y[ly + 2], z[lz + 2];
memset(z, 0, sizeof z);
string ans = "";
for (int i = 0, j = lx; i < lx; i++, j--)
x[j] = a[i] - '0';
for (int i = 0, j = ly; i < ly; i++, j--)
y[j] = b[i] - '0';
for (int i = 1; i <= lx; i++) // 不进位的乘法
for (int j = 1; j <= ly; j++)
z[i + j - 1] += x[i] * y[j];
// 进位
int w = 0;
for (int i = 1; i <= lz; i++) {
z[i] += w;
w = z[i] / 10;
z[i] %= 10;
}
// 处理剩余进位
while (w) {
z[++lz] += w;
w = z[lz] / 10;
z[lz] %= 10;
}
// 去除前缀 0
while (z[lz] == 0)
lz--;
// 存放答案
for (int i = lz; i >= 1; i--)
ans += z[i] + '0';
return ans;
}
string _max(string a, string b) { // 最大值
int la = a.length(), lb = b.length();
if (la < lb) return b;
else if (la > lb) return a;
// 逐位比较
for (int i = 0; i < la; i++) {
if (a[i] < b[i]) return b;
else if (a[i] > b[i]) return a;
}
return a;
}
void dfs(int p, int lp, int cnt, string sum) {
// 当前第 p 层,上一个乘号在第 lp 层,已经确定了 cnt 个乘号,目前乘积 sum
if (cnt == k) { // 完成
sum = mul(sum, data(lp + 1, n));
ans = _max(ans, sum);
return;
}
if (p >= n) return;
dfs(p + 1, p, cnt + 1, mul(sum, data(lp + 1, p))); // 添加乘号
dfs(p + 1, lp, cnt, sum); // 不添加乘号
}
int main() {
cin >> n >> k >> t;
for (int i = 0; i < n; i++)
s[i + 1] = t[i];
dfs(1, 0, 0, "1");
cout << ans << endl;
return 0;
}
提交记录
没想到没加剪枝的裸 dfs 竟然能过。
数据范围太水了。
追求完美的我又写了剪枝。
100 pts
思路
暴力 dfs + 可行性剪枝 + 高精度。
代码演示
#include <iostream>
#include <cstring>
using namespace std;
#define MAXN 45
#define MAXK 10
int n, k;
string t, ans = "0";
char s[MAXN];
string data(int l, int r) { // 提取 s[l, r]
if (l > r) return "1";
string num = "";
for (int i = l; i <= r; i++)
num += s[i];
return num;
}
string mul(string a, string b) { // 高精乘
if (a == "0" || b == "0") return "0";
int lx = a.length(), ly = b.length(), lz = lx + ly;
int x[lx + 2], y[ly + 2], z[lz + 2];
memset(z, 0, sizeof z);
string ans = "";
for (int i = 0, j = lx; i < lx; i++, j--)
x[j] = a[i] - '0';
for (int i = 0, j = ly; i < ly; i++, j--)
y[j] = b[i] - '0';
for (int i = 1; i <= lx; i++) // 不进位的乘法
for (int j = 1; j <= ly; j++)
z[i + j - 1] += x[i] * y[j];
// 进位
int w = 0;
for (int i = 1; i <= lz; i++) {
z[i] += w;
w = z[i] / 10;
z[i] %= 10;
}
// 处理剩余进位
while (w) {
z[++lz] += w;
w = z[lz] / 10;
z[lz] %= 10;
}
// 去除前缀 0
while (z[lz] == 0)
lz--;
// 存放答案
for (int i = lz; i >= 1; i--)
ans += z[i] + '0';
return ans;
}
string _max(string a, string b) { // 最大值
int la = a.length(), lb = b.length();
if (la < lb) return b;
else if (la > lb) return a;
// 逐位比较
for (int i = 0; i < la; i++) {
if (a[i] < b[i]) return b;
else if (a[i] > b[i]) return a;
}
return a;
}
void dfs(int p, int lp, int cnt, string sum) {
// 当前第 p 层,上一个乘号在第 lp 层,已经确定了 cnt 个乘号,目前乘积 sum
if (cnt == k) { // 完成
sum = mul(sum, data(lp + 1, n));
ans = _max(ans, sum);
return;
}
if (p >= n) return;
if (cnt > k) return; // 可行性剪枝
if (n - p + cnt < k) return; // 可行性剪枝
dfs(p + 1, p, cnt + 1, mul(sum, data(lp + 1, p))); // 添加乘号
dfs(p + 1, lp, cnt, sum); // 不添加乘号
}
int main() {
cin >> n >> k >> t;
for (int i = 0; i < n; i++)
s[i + 1] = t[i];
dfs(1, 0, 0, "1");
cout << ans << endl;
return 0;
}
提交记录
个人 OI 水平有限,请见谅。