目录
题目
P1874 快速求和
算法标签: 动态规划, 线性 d p dp dp
思路
求的是最少组成 n n n的加法次数, 对于当前数字序列可以设计状态表示 f [ i ] [ j ] f[i][j] f[i][j]表示考虑前 i i i个字符, 并且和是 j j j的所有方案中加法次数最小的方案, 那么就要考虑状态转移, 按照最后一步的思想, 可以将集合划分为最后一个数字的长度, 也就是最后一个数字是什么, 算法时间复杂度 O ( n × l e n ) O(n \times len) O(n×len)
代码
#include <iostream>
#include <algorithm>
#include <cstring>using namespace std;typedef long long LL;
const LL INF = 5e18, N = 55, M = 1e5 + 40;string s;
LL w[N], nums[N][N], cnt;
LL sum, n, f[N][M];
bool vis;int main() {ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);cin >> s >> sum;n = s.size();//去除前导0for (int i = 0; s[i]; ++i) {if (s[i] != '0') vis = true;if (vis) w[++cnt] = s[i] - '0';}if (cnt == 0) w[++cnt] = 0;//预处理两个位置直接组成的数字大小for (int i = 1; i <= cnt; ++i) {nums[i][i] = w[i];for (int j = i; j - i <= 11 && j <= cnt; ++j) {nums[i][j] = nums[i][j - 1] * 10 + w[j];}}//初始化dpfor (int i = 0; i <= cnt + 1; ++i) {for (int j = 0; j <= sum + 1; ++j) f[i][j] = INF;}f[0][0] = 0;for (int i = 1; i <= cnt; ++i) {//枚举最后一个数字的大小for (int k = 1; k <= 11; ++k) {if (i >= k) {LL curr = nums[i - k + 1][i];for (LL j = curr; j <= sum; ++j) {f[i][j] = min(f[i][j], f[i - k][j - curr] + 1);}}}}int ans;if (f[cnt][sum] > cnt) ans = -1;else ans = f[cnt][sum] - 1;cout << ans << "\n";return 0;
}